- 是否因?yàn)榭蛻魷?zhǔn)備報(bào)價(jià)而占用大量時(shí)間
- 是否總是在調(diào)Excel列寬行高搞得精疲力盡
- 是否為產(chǎn)品在報(bào)價(jià)表上需要分類感到焦頭爛額
- 是否想有一個(gè)助手把這些機(jī)械的工作做得又快又好
下面這段python代碼可以幫你解決以上所有問題,只用1秒鐘。
先看下效果:
https://www.bilibili.com/video/BV1Be411x7j7/
-
準(zhǔn)備好基礎(chǔ)數(shù)據(jù)表
0.png -
客戶信息照舊填寫,把客戶的需要的型號(hào)以及數(shù)量填好;0-2.png
運(yùn)行程序。
完成!

0-3.png
接下來(lái)我們來(lái)看看如何實(shí)現(xiàn)。
需要準(zhǔn)備的基礎(chǔ)文件主要是產(chǎn)品信息統(tǒng)計(jì)表,python程序會(huì)根據(jù)客戶的新需求在統(tǒng)計(jì)表中自動(dòng)尋找匹配。此處我們將所有的信息都匯總在 ’DATA.xlsx‘ 下的'Pool 產(chǎn)品明細(xì)'中,并將客戶需求填寫在'AUTO PI DOCUMENT.xlsx'的'Sheet1'中,程序根據(jù)此表在 ’DATA.xlsx‘ 下的'Pool 產(chǎn)品明細(xì)'中做出匹配,然后生成新的'PI’ 作為發(fā)票草本。
準(zhǔn)備好基礎(chǔ)文件后,我們正式進(jìn)入編程
我們會(huì)用到openpyxl庫(kù)來(lái)處理Excel,及Font, Alignment,Border,Side方法來(lái)調(diào)整字體,格式,以及繪制表格。
載入庫(kù)函數(shù)
import openpyxl
from openpyxl.styles import Font, Alignment, Border, Side
- 載入需要使用的文檔,
# 打開要求文檔
wbData = openpyxl.load_workbook('AUTO PI DOCUMENT.xlsx')
wsData = wbData['Sheet1']
wbSource = openpyxl.load_workbook('DATA.xlsx', data_only=True)
wsSource = wbSource['Pool 產(chǎn)品明細(xì)']
這里注意在載入'Data.xlsx'的時(shí)候,因?yàn)楸碇写嬖诖罅抗竭M(jìn)行計(jì)算,所以需要讀取單元格內(nèi)的值,而不要提取公式,所以后面加上了data_only=True
新建一個(gè)工作簿,在新工作簿中創(chuàng)建名為‘PI’的工作表
# 新建文檔
wb = openpyxl.Workbook()
# Sheet新建與命名
wsPI = wb.active
wsPI.title = 'PI'
- 定義一下將會(huì)需要的字體與單元格邊框格式
# 定義字體及邊框格式
fontTitle = Font(name='Arial', bold=True, size=14)
fontObj1 = Font(name='Arial', size=9)
fontObj2 = Font(name='Arial', bold=True, size=8)
border_set = Border(left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin'))
- 接下來(lái)需要制作幾個(gè)內(nèi)置函數(shù)來(lái)方便程序編寫
由于期望將客戶需要的產(chǎn)品根據(jù)系列不同來(lái)分開,所以需要提取系列的編號(hào),提取產(chǎn)品的型號(hào),以及匹配信息并返回相應(yīng)值的三個(gè)函數(shù)
- 提取系列的編號(hào)函數(shù)
- 由于例子中的產(chǎn)品主要是開關(guān)插座產(chǎn)品,編號(hào)'C301', ' VP405',規(guī)則是第一位或前兩位字母表示系列,后面3位表示型號(hào),即便后面再加上顏色參數(shù),總位數(shù)也不會(huì)超過8位。這里我首先需要判斷產(chǎn)品型號(hào)是否小于8位,且第一位是字母,第三四位是數(shù)字。然后繼續(xù)判斷,如果第二位是數(shù)字,則系列編號(hào)只有1位,如果第二位也是字母,那么就返回前兩位作為系列編號(hào)
# 判斷是不是開關(guān),并提取系列
def isswich (CODE):
if len(CODE) < 8 and CODE[0].isalpha() and CODE[2:4].isdecimal():
if CODE[1].isdecimal():
return CODE[0]
else:
return CODE[0:2]
else:
return False
- 提取產(chǎn)品型號(hào)函數(shù)
- 如果輸入的產(chǎn)品是開關(guān)插座,那么將系列型號(hào)去除,接下來(lái)三位是編號(hào),返回編號(hào)
# 提取產(chǎn)品型號(hào)
def code(CODE):
if isswich(CODE):
return CODE[len(isswich(CODE)): len(isswich(CODE))+3]
3.匹配產(chǎn)品信息函數(shù)
- 需要根據(jù)產(chǎn)品的型號(hào)’item',匹配出需要的信息‘info'
# 匹配產(chǎn)品信息
def match(item,info):
title_col = 0
for title in range(1, wsSource.max_column+1):
if wsSource.cell(row=1, column=title).value == info:
title_col = title
if isswich(item):
# 在database里尋找相應(yīng)的系列
for row in range(1, wsSource.max_row+1):
if wsSource.cell(row=row, column=4).value == isswich(item) and \
wsSource.cell(row=row, column=5).value == int(code(item)):
return wsSource.cell(row=row, column=title_col).value
- 然后因?yàn)樵赑I中根據(jù)系列進(jìn)行分類,那就先將所有系列抽取,放在一個(gè)list中
# 找出所有 range
AllRange = set()
for i in range(16, wsData.max_row+1):
# 只有當(dāng)item字符小于8個(gè),首位是字母,第3,4位為數(shù)字,被認(rèn)作是開關(guān)插座
if isswich(wsData['C' + str(i)].value):
rangeObj = str(wsData['C' + str(i)].value)[0:2]
if rangeObj[1].isdecimal():
rangeObj = rangeObj[0]
AllRange.add(rangeObj)
AllRange = list(AllRange)
- 接下來(lái)把所有產(chǎn)品分門別類的放在各個(gè)系列中,組成一個(gè)產(chǎn)品字典。
# 創(chuàng)建 產(chǎn)品字典
AllItem = {}
for Range in AllRange:
for itemRow in range(16, wsData.max_row+1):
if isswich(wsData['C'+str(itemRow)].value) == Range:
ITEM = wsData.cell(row=itemRow, column=3).value
qty = wsData.cell(row=itemRow, column=4).value
AllItem.setdefault(Range, {})
AllItem[Range].setdefault(ITEM, qty)
- 準(zhǔn)備工作都做好了,接下來(lái)開始正式編輯PI
- 設(shè)置列寬
# 編輯文檔
# 列寬設(shè)置
wsPI.column_dimensions['A'].width = 3
wsPI.column_dimensions['B'].width = 9
wsPI.column_dimensions['C'].width = 35
wsPI.column_dimensions['D'].width = 10
wsPI.column_dimensions['E'].width = 12
wsPI.column_dimensions['F'].width = 9.5
wsPI.column_dimensions['G'].width = 6.5
- 填寫公司名稱等表頭并調(diào)整字體格式
# 第一行 公司名稱
wsPI['A1'].value = wsData['B3'].value
wsPI['A1'].font = fontTitle
wsPI.merge_cells('A1:G1')
wsPI['A1'].alignment = Alignment(horizontal='center')
# 第二行 地址
wsPI['A2'].value = wsData['B4'].value
wsPI['A2'].font = fontObj1
wsPI.merge_cells('A2:G2')
wsPI['A2'].alignment = Alignment(horizontal='center')
# 第三行 電話
wsPI['A3'].value = wsData['B5'].value
wsPI['A3'].font = fontObj1
wsPI.merge_cells('A3:G3')
wsPI['A3'].alignment = Alignment(horizontal='center')
# 第四行 表名
wsPI['A4'].value = wsData['B6'].value
wsPI['A4'].font = fontTitle
wsPI.merge_cells('A4:G4')
wsPI['A4'].alignment = Alignment(horizontal='center')
- 第二部分填寫訂單相關(guān)信息并調(diào)整字體格式
- 填寫表頭
# 第二部分,客戶及合同信息
# 2.1 填寫訂單相關(guān)信息
wsPI['A5'].value = 'To: ' + str(wsData['B7'].value)
wsPI['A6'].value = 'SHIPMENT: ' + str(wsData['B8'].value)
wsPI['A7'].value = 'PAYMENT: ' + str(wsData['B9'].value)
wsPI['A8'].value = 'AMOUNT AND QUANTITY: ' + str(wsData['B10'].value)
wsPI['E5'].value = 'INVOICE NO.: ' + str(wsData['B1'].value)
wsPI['E6'].value = 'DATE: ' + str(wsData['B11'].value)
wsPI['E7'].value = 'PORT OF DESTINATION: ' + str(wsData['B12'].value)
wsPI['E8'].value = 'PORT OF LOADING: ' + str(wsData['B13'].value)
# 2.2 合并單元格
wsPI.merge_cells('A5:D5')
wsPI.merge_cells('A6:D6')
wsPI.merge_cells('A7:D7')
wsPI.merge_cells('A8:D8')
wsPI.merge_cells('E5:G5')
wsPI.merge_cells('E6:G6')
wsPI.merge_cells('E7:G7')
wsPI.merge_cells('E8:G8')
for i in range(5, 9):
wsPI["A"+str(i)].font = fontObj1
wsPI["E" + str(i)].font = fontObj1
- 完成表格主體
# 填寫產(chǎn)品相關(guān)信息
# 標(biāo)題
TitleList = ['No.', 'ITEM', 'DESCRIPTION', 'QUANTITY', 'UNIT PRICE',
'TOTAL', 'VOLUME', 'CTN', 'PCS/CTN', 'CBM/CTN']
for i in range(1, 11):
wsPI.cell(row=9, column=i).value = str(TitleList[i - 1])
for i in range(1, 11):
wsPI.cell(row=9, column=i).font = fontObj2
wsPI.cell(row=9, column=i).alignment = Alignment(horizontal='center')
# 內(nèi)容
Line = 10
for R in AllRange:
wsPI.cell(row=Line, column=1).value = R+' Range'
Line += 1
ItemList = list(AllItem[R])
for eachItem in ItemList:
wsPI.cell(row=Line, column=2).value = eachItem
wsPI.cell(row=Line, column=4).value = AllItem[R][eachItem]
wsPI.cell(row=Line, column=3).value = match(wsPI.cell(row=Line, column=2).value, 'DESCRIPTION')
wsPI.cell(row=Line, column=5).value = match(wsPI.cell(row=Line, column=2).value, 'PRICE')
wsPI.cell(row=Line, column=9).value = match(wsPI.cell(row=Line, column=2).value, 'QTY/CTN')
wsPI.cell(row=Line, column=10).value = match(wsPI.cell(row=Line, column=2).value, 'VOLUME')
wsPI.cell(row=Line, column=6).value = '=round(D{}*E{},2)'.format(Line, Line)
wsPI.cell(row=Line, column=8).value = '=D{}/I{}'.format(Line, Line)
wsPI.cell(row=Line, column=7).value = '=round(H{}*J{},3)'.format(Line, Line)
wsPI.cell(row=Line, column=4).number_format = '#,##0" PCS"'
wsPI.cell(row=Line, column=5).number_format = '$#,##0.00'
wsPI.cell(row=Line, column=6).number_format = '$#,##0.00'
wsPI.cell(row=Line, column=7).number_format = '#,##0.000'
wsPI.cell(row=Line, column=8).number_format = '#" CTN"'
wsPI.cell(row=Line, column=9).number_format = '#" PCS"'
wsPI.cell(row=Line, column=10).number_format = '#,##0.000'
Line += 1
- 完成表格匯總行
MaxRow = wsPI.max_row
wsPI.cell(row=MaxRow+1, column=3).value = 'TOTAL'
wsPI.cell(row=MaxRow+1, column=4).value = '=sum(D10:D{})'.format(MaxRow)
wsPI.cell(row=MaxRow+1, column=5).value = 'FOB WENZHOU'
wsPI.cell(row=MaxRow+1, column=6).value = '=sum(F10:F{})'.format(MaxRow)
wsPI.cell(row=MaxRow+1, column=7).value = '=sum(G10:G{})'.format(MaxRow)
wsPI.cell(row=MaxRow+1, column=8).value = '=sum(H10:H{})'.format(MaxRow)
wsPI.cell(row=wsPI.max_row, column=3).font = fontObj2
wsPI.cell(row=wsPI.max_row, column=3).alignment = Alignment(horizontal='center')
wsPI.cell(row=wsPI.max_row, column=4).number_format = '#,##0" PCS"'
wsPI.cell(row=wsPI.max_row, column=5).font = fontObj2
wsPI.cell(row=wsPI.max_row, column=5).alignment = Alignment(horizontal='center')
wsPI.cell(row=wsPI.max_row, column=6).number_format = '$#,##0.00'
wsPI.cell(row=wsPI.max_row, column=7).number_format = '#,##0.000'
wsPI.cell(row=wsPI.max_row, column=8).number_format = '#" CTN"'
- 繪制表格,調(diào)整部分需要加粗字體
for row in range(10, wsPI.max_row+1):
for column in range(1,8):
wsPI.cell(row=row,column=column).border = border_set
wsPI.cell(row=row, column=column).font = fontObj1
for row in range(9,wsPI.max_row+1):
wsPI.cell(row=row, column=1).font = fontObj2
for column in range(1,8):
wsPI.cell(row=9, column=column).border = border_set
- 結(jié)尾 合同雙方
# 結(jié)尾
lastRow = wsPI.max_row
wsPI.cell(row=lastRow+2, column=1).value = 'ACCEPTED:'
wsPI.cell(row=lastRow+3, column=1).value = wsData['B7'].value
wsPI.cell(row=lastRow+2, column=4).value = 'OFFERED:'
wsPI.cell(row=lastRow+3, column=4).value = wsData['B3'].value
for k in range(lastRow+2, lastRow+4):
wsPI["A"+str(k)].font = fontObj2
wsPI["D" + str(k)].font = fontObj2
- 保存文件名(合同編號(hào)及賣方信息)
# 文件保存
wb.save("{}-{}.xlsx".format(wsData["B2"].value, wsData["B1"].value))
- 以上便是合同編寫簡(jiǎn)易的小程序
- (代碼中會(huì)有冗余,和不合理不嚴(yán)謹(jǐn)?shù)倪壿?,還望高手指正)

