-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinvoice
executable file
·208 lines (198 loc) · 13.3 KB
/
invoice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/usr/bin/python3
import pdfkit
import psycopg2
import os
import psutil
import json
import configparser
from datetime import date, datetime, timedelta
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
config = configparser.ConfigParser()
config.read('.config')
def generate(company, addr, invoiceNo, today, duedate, items):
options = {
'page-size': 'A4',
'margin-top': '0in',
'margin-right': '0in',
'margin-bottom': '0in',
'margin-left': '0in',
'encoding': "UTF-8",
'custom-header' : [
('Accept-Encoding', 'gzip')
],
'cookie': [
('cookie-name1', 'cookie-value1'),
('cookie-name2', 'cookie-value2'),
],
'no-outline': None
}
table = ''
quantity = 0
amount = 0
for item in items:
table += '<div class="table-tr"><div class="inline left col-15">'+item[0]+'</div><div class="inline left col-50">'+item[1]+'</div><div class="inline center col-10">$'+'{:,.2f}'.format(item[2])+'</div><div class="inline center col-10">'+'{:,.2f}'.format(item[3])+'</div><div class="inline right col-15">$'+'{:,.2f}'.format(item[2]*item[3])+'</div></div>'
quantity += item[3]
amount += item[2]*item[3]
html = '<!DOCTYPE html><html><head><style>@import url(https://fonts.googleapis.com/css2?family=Marck+Script:wght@400;900&family=Roboto:wght@400;900&display=swap); body{ font-family: "Roboto"; font-size: 1.2rem; height: 100%;} .header{ position: relative; top: 0; left: 0; margin: -8px -8px; width: calc(100% - 34px); height: 128px; background-color: #29a0e6; padding: 25px;} .header img{ position: absolute; top: 15px; left: 45px;} .header .title{ font-size: 4rem; font-family: "Marck Script"; font-weight: 900; color: white; text-align: left; position: absolute; bottom: 15px;} .header .info{ font-size: 1rem; color: white; text-align: right; margin-top: 12px; line-height: 1.5rem;} .header a{ text-decoration: none; color: white;} .bill{ position: relative; width: calc(100% - 34px); height: 150px; top: 0; left: 0; margin: -8px -8px; padding: 25px; border-bottom: 1px solid #d1d1d1;} .bill .leftpart{ position: absolute; top: 25px; left: 25px; text-align: left;} .bill .leftpart .to{ line-height: 2rem; font-size: 1.2rem;} .bill .leftpart .company{ line-height: 2rem; font-size: 1.2rem; font-weight: 900;} .bill .leftpart .addr{ margin-top: 5px; line-height: 1.5rem; font-size: 1rem;} .bill .rightpart{ position: absolute; top: 25px; right: 25px; text-align: right; font-size: 1rem; line-height: 1.6rem;} .bold{ font-weight: 900;} .left{ text-align: left;} .right{ text-align: right;} .center{ text-align: center;} .details{ width: calc(100% - 34px); padding: 25px; position: relative; top: 0; left: 0; margin: -8px -8px;} .details .table-header{ border-bottom: 1px solid #d1d1d1; padding-bottom: 10px;} .details .table-body, .details .table-total{ padding-top: 10px; border-bottom: 1px solid #d1d1d1;} .details .table-balance{ position: relative; width: 45%; left: 55%; padding-top: 10px; border-bottom: 1px solid #d1d1d1;} .details .table-tr{ padding-bottom: 10px;} .inline{ display: inline-block;} .col-10{ width: 12%;} .col-60{ width: 61%;} .col-15{ width: 20%;} .col-50{ width: 33.4%;} .footer{ position: relative; top: 0; left: 0; margin: -8px -8px; width: calc(100% + 16px);} .footer .inner{ position: absolute; top: 0; left: 0; width: calc(100% - 50px); height: 78px; background-color: #29a0e6; padding: 25px;} .footer .inner .leftpart{ position: absolute; top: 0; left: 0; width: 60%; height: 98px; background-color: #e3f6ff; padding: 15px 15px; font-size: 1rem;} .footer .inner .rightpart{ position: absolute; right: 0; top: 0; width: calc(40% - 60px); height: 78px; padding: 25px 15px;} </style></head><body><div class="header"><img src="https://www.iceloof.com/assets/icons/icon.png" /><div class="title">Invoice</div><div class="info"><div>Hurin Hu</div><div>[email protected]</div><div>+64 2102470043</div><div><a href="https://www.iceloof.com" target="_blank" >https://www.iceloof.com</a ></div><div>GST number: <b><i>119-432-006</i></b></div></div></div><div class="bill"><div class="leftpart"><div class="to">BILL TO:</div><div class="company">'+company+'</div><div class="addr">'+addr+'</div></div><div class="rightpart"><div class="bold">INVOICE #</div><div>INV'+str(invoiceNo)+'</div><div class="bold">DATE</div><div>'+today+'</div><div class="bold">DUE</div><div>'+duedate+'</div></div></div><div class="details"><div class="table-header"><div class="inline left bold col-15">Date</div><div class="inline left bold col-50">Item</div><div class="inline center bold col-10">Rate</div><div class="inline center bold col-10">Quantity</div><div class="inline right bold col-15">Amount(NZD)</div></div><div class="table-body">'+table+'</div><div class="table-total"><div class="table-tr"><div class="inline left bold col-15">Sub Total</div><div class="inline left col-50"></div><div class="inline center col-10"></div><div class="inline center col-10">'+'{:,.2f}'.format(quantity)+'</div><div class="inline right col-15">$'+'{:,.2f}'.format(amount)+'</div></div><div class="table-tr"><div class="inline left bold col-15">GST</div><div class="inline left col-50"></div><div class="inline center col-10"></div><div class="inline center col-10">+15%</div><div class="inline right col-15">$'+'{:,.2f}'.format(amount*0.15)+'</div></div></div><div class="table-balance"><div class="table-tr"><div class="inline left bold col-60">Balance Due</div><div class="inline right col-50">$'+'{:,.2f}'.format(amount*1.15)+'</div></div></div></div><div class="footer"><div class="inner"><div class="leftpart"><div style="font-weight: 900; line-height: 1.5rem">Payment Instructions </div><div><div style=" font-weight: 900; line-height: 1.2rem; display: inline-block; width: 80px; " >To </div>Hurin Hu </div><div><div style=" font-weight: 900; line-height: 1.2rem; display: inline-block; width: 80px; " >Bank </div>Westpac NZ </div><div><div style=" font-weight: 900; line-height: 1.2rem; display: inline-block; width: 80px; " >Account </div>03-1399-0143883-001 </div><div><div style=" font-weight: 900; line-height: 1.2rem; display: inline-block; width: 80px; " >Ref. </div>'+str(invoiceNo)+' </div></div><div class="rightpart"><div style=" text-align: right; font-size: 1.2rem; line-height: 2rem; color: #fff; " >Total </div><div style=" text-align: right; font-size: 2rem; font-weight: 900; color: #fff; " id="test" >(NZD)$'+'{:,.2f}'.format(amount*1.15)+' </div></div></div></div><script>document.getElementsByClassName("footer")[0].style.marginTop="150px"; </script></body></html>'
pdfkit.from_string(html, '/invoices/INV'+str(invoiceNo)+'.pdf', options=options)
return amount
def sendEmail(invoiceNo, to, amount, duedate):
try:
html = "<html><head><title>Invoice</title></head><body style='font-family: Comic Sans MS'>Hi,<br><p>This is your invoice for the service, the due balance is $"+'{:,.2f}'.format(amount*1.15)+", please follow the instruction to pay before due date "+duedate+". If you have any questions, please contact me as soon as possible.<br><p>Kind regards, <br>Hurin Hu<br><a href=\"mailto:[email protected]\">[email protected]</a><br><a href=\"https://www.iceloof.com\">https://www.iceloof.com</a></p></body></html>"
filename = '/invoices/INV'+str(invoiceNo)+'.pdf'
msg = MIMEMultipart('mixed')
msg['Subject'] = "Invoice ("+str(invoiceNo)+")"
msg['From'] = "Iceloof Admin<"+config['email']['admin']+">"
msg['To'] = to
msg['Cc'] = config['email']['to']
part2 = MIMEText(html, 'html')
fp=open(filename,'rb')
att = MIMEApplication(fp.read(),_subtype="pdf")
fp.close()
att.add_header('Content-Disposition','attachment',filename='INV'+str(invoiceNo)+'.pdf')
msg.attach(att)
msg.attach(part2)
s = smtplib.SMTP_SSL(host=config['email']['host'],port=config['email']['port'])
s.ehlo()
s.login(config['email']['email'],config['email']['password'])
s.sendmail(msg['From'], [to, msg['Cc']], msg.as_string())
s.quit()
except:
menu()
def saveDB(invoiceNo, company, addr, today, duedate, items, paid, email):
try:
sql = 'INSERT INTO invoice (invoiceno, company, address, issuedate, duedate, items, paid, email) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
con = psycopg2.connect( host=config['db']['host'], user=config['db']['user'], password=config['db']['pwd'], dbname="postgres", options="-c search_path=dbo,invoice" )
cur = con.cursor()
cur.execute(sql, (invoiceNo, company, addr, today, duedate, json.dumps(items), paid, email))
con.commit()
cur.close()
con.close()
except Exception as e:
print(e)
menu()
def getLatestInvoiceNo(d=0):
try:
d1 = d*1000+1
d2 = d*1000+999
sql = "SELECT INVOICENO FROM INVOICE WHERE INVOICENO>="+str(d1)+" AND INVOICENO<="+str(d2)+" ORDER BY INVOICENO DESC LIMIT 1"
if d == 0:
sql = "SELECT INVOICENO FROM INVOICE ORDER BY INVOICENO DESC LIMIT 1"
con = psycopg2.connect( host=config['db']['host'], user=config['db']['user'], password=config['db']['pwd'], dbname="postgres", options="-c search_path=dbo,invoice" )
cur = con.cursor()
cur.execute(sql)
result = cur.fetchall()
cur.close()
con.close()
invoiceNo = int(date.today().strftime('%Y%m%d001'))
if len(result)>0:
if result[0][0] >= invoiceNo:
invoiceNo = result[0][0]+1
elif d != 0:
invoiceNo = d*1000+1
return invoiceNo
except:
return None
def getItem():
try:
print()
print('Add new item')
d = input('Date: ')
i = input('Item: ')
r = float(input('Rate: '))
q = float(input('Qty: '))
return [d,i,r,q]
except:
print('Input Error!')
return getItem()
def generateInput():
try:
print('---------------------------------------------------')
company = input('Client: ')
addr = input('Address: ')
today = input('Issue date (yyyymmdd/skip): ')
if len(today) == 0 or today == 'skip':
today = date.today().strftime('%b %d, %Y')
invoiceNo = getLatestInvoiceNo()
else:
invoiceNo = getLatestInvoiceNo(int(today))
today = datetime.strptime(today, '%Y%m%d').strftime('%b %d, %Y')
if invoiceNo is None:
print('invoiceNo ERROR!')
menu()
duedate = input('Due date (yyyymmdd/skip): ')
if len(duedate) == 0 or duedate == 'skip':
duedate = (date.today()+timedelta(days=7)).strftime('%b %d, %Y')
else:
duedate = datetime.strptime(duedate, '%Y%m%d').strftime('%b %d, %Y')
email = input('Email to: ')
paid = input('Paid (Y/N): ')
if paid == 'Y':
paid = True
else:
paid = False
num = int(input('No. of items: '))
items = []
for i in range(num):
item = getItem()
items.append(item)
print('---------------------------------------------------')
print('Preview')
print('Client: '+company)
print('Address: '+addr)
print('Issue Date: '+today)
print('Due Date: '+duedate)
print('InvoiceNo.: INV'+str(invoiceNo))
print('Paid: '+str(paid))
print('Email: '+email)
print ("{:<15} {:<25} {:<16} {:<15} {:<21}".format('Date','Item','Rate','Quantity','Amount'))
balance = 0
for item in items:
print ("{:<15} {:<25} ${:<15} {:<15} ${:<20}".format(item[0],item[1],round(item[2],2),round(item[3],2),round(item[2]*item[3],2)))
balance += item[2]*item[3]
print('Sub total: $'+str(round(balance,2)))
print('GST: $'+str(round(balance*0.15,2)))
print('Balance Due: $'+str(round(balance*1.15,2)))
print('---------------------------------------------------')
confirm = input('Confirm (Y/N):')
if confirm == 'Y':
print('---------------------------------------------------')
print('Generating invoice ...')
try:
amount = generate(company, addr, invoiceNo, today, duedate, items)
except:
print('Input Error!')
return menu()
print('Invoice INV'+str(invoiceNo)+' generated')
if len(email) != 0:
sendEmail(invoiceNo, email, amount, duedate)
print('Email sent to '+email)
saveDB(invoiceNo, company, addr, today, duedate, items, paid, email)
menu()
else:
menu()
except:
print('Input Error!')
menu()
def menu():
print('---------------------------------------------------')
print('Welcome to Invoice System')
print('1. Generate new invoice')
print('2. Exit')
print('---------------------------------------------------')
try:
menu = int(input('Please ENTER menu: '))
if menu == 1:
generateInput()
elif menu == 2:
current_system_pid = os.getpid()
ThisSystem = psutil.Process(current_system_pid)
ThisSystem.terminate()
except:
print('Input ERROR!')
menu()
if __name__ == "__main__":
menu()