总算跑通了,可我觉得逻辑和语法真屎
本帖最后由 13832811375 于 2020-11-30 16:32 编辑一、“请输入操作人姓名或编号:”:可以为字符和数字,但不能是空格也不能直接回车;此部分内容会默认添加到“说明”栏,如果“说明”里直接回车没有输入内容的话。
二、“售卖还是入库?售卖请直接回车;入库请输入"RK"后回车。”:
1、商品要先入库(“RK”是英文大写,输入时不用输入引号),然后才能进行售卖;商品没有入库就进行售卖的话,程序会提示后退出。
2、输入“RK”回车后开始入库,在此处任何其它输入,包括回车、空格等,都会进行“售卖”界面(为方便厨师操作);
2、“售卖”和“入库”以及“RK”这些关键字应做成特别颜色,尚待改进;
三、“请扫描商品条码:”:直接用扫码枪扫码一个(件/类)商品。识别正确的条码应该是13位数字(目前实验阶段为手输3位阿拉伯数字),不符合这个规范的一律拒绝输入。成功后,自动进入下一步;
四、“请输入商品名称:”:可输入字符或者数字,不能是空格或者回车;输入空格或者回车时,程序会要求重新输入“商品名称”。
五、“请输入入库数量:”:必须输入整数阿拉伯数字,不能是空格或者回车;输入空格或者回车时,程序会要求重新输入“入库数量”(每次入库一种商品,商品可以是任意个。注意要点齐商品数量,如果有遗落,重复入库的话,会因为是重复录入而删除所有重复项)。
六、“请输入商品价格:”:只能是整数或者小数的阿拉伯数字,不能是空格或者回车;输入小数时,小数点儿应为英文半角的“.”,但程序里稍做优化,误输入“。”也可以正常计算;输入普通字符、空格或者回车时,程序会要求重新输入“商品价格”。
七、“说明:”:此处输入无限制,也可以为空或者直接回车。如果为空或直接回车,此处将填入第一项中输入的“操作人姓名或编号”。
八、“说明”完成后,会显示刚才输入的这一条共六项内容(多了一项操作时间),并提示检查输入是否正确。并自动回到开始,要求重新录入。此处的特殊字符,如“y”、“Y”和“回车”等,应该做特殊颜色处理,未完成。
1、如果输入正确,可以输入除了“y”、“Y”和“是”开头儿的任意内容,最方便的是直接按回车键,然后进入下一条的输入;
2、按“y”、“Y”(就是yes/YES的第一个字母)或“是”的话,程序会认上条内容输入有误,会删除刚才输入的这一条的部分内容。
3、如果按“y”、“Y”或“是”,会直接让输入“商品名称”、“价格”和“数量”,不需要再扫码,因为刚才已经判断扫码了。“说明”也不需要。
九、“是否继续入库:”:是询问是否对下一个(件/类)商品开始扫码。比如上次可能一次入库一根火腿,也可是能15瓶可乐,但往下要开始扫别的商品了(最好不要重复)。
1、此处,如果继续入库其它商品,可以直接回车;
2、如果结束入库,按“n”、“N”或“否”即可。注意,由于判断逻辑是只判断最左侧第一个字符,所以,即使你输入的是“否否...”、“Num...”或者“nnnnn....”,程序也会认为是结束入库操作,开始下一步。
3、单次入库多件商品的去重处理:由于一次入库有可能要操作几十件商品,有可能会误拿,或者操作员自己也不记得是否入过某件商品,因此入库操作设计了去重功能。不需要操作员做操作,程序会自己根据“商品ID”,也就是扫码结果,而不是商品名称来判断。发现有重复的“商品ID”后,会把重复项全删除。操作员只需记住提示的内容,然后再打开程序重新录入该商品即可。
十一、按“n”等并回车后,程序保存输入结果到“D:\CHRKGL”文件夹中,是两个文件,一个是“总库.xlsx”,另一个是带有时间戳的“...入库.xlsx”文件。“总库.xlsx”既是以后售卖的根据,也是每次入库的汇总,是关键文件。每次入库都会改变其中的数据,每次售卖也会改变其中的数据,必须保存好。生成的带时间戳的文件仅供参考,总数正确的话,可以保存备查,也可以删除。
十二、第二次入库时,程序会自动读取“总库.xlsx”数据,并根据是否已经入库更新总库数据(主要是数量和金额,价格如有变动会更新为后新价格)。
十三、如果在开始时直接回车(输入除了“RK”以外的任意字符回车也行),可以自动进入“售卖”环节。
十四、按照提示按下任意键后,程序先显示一遍目前的“总库.xlsx”中的数据,然后开始等待扫码枪扫码。扫码后,会根据多种因素做判断:
1、如果找不到“总库.xlsx”文件,会提示后退出程序;
2、如果此时误按回车、空格等,程序会提示重新扫码;
3、如果扫码错误(即扫码结果不是个13位长的数字),程序会提示重新扫码;
4、如果扫码正确,但商品不在“总库.xlsx”中,程序会提示后结束,请重新打开程序进行入库操作;
5、排除以上异常后,显示“应收XXX元”
6、售卖结束后,直接在“请扫描商品条码:”后直接输入“0”或任意以“0”开始字符,程序会提示“按任意键结束售卖,退出程序”。
十五、程序在退出过程中,会显示本次售卖清单,包括每种商品卖出多少及金额,和售卖本次售卖总钱数,以及根据本次售卖对“总库.xlsx”的核减,主要是“总库.xlsx”中的“库存数量”和“库存金额”。
十六、本次售卖会保存为一个带当前时间戳的EXCEL文件,新总库文件也随即保存。整个程序正常结束。
十七、语音和图形界面,还不会加。想最后搞成鼠标操作的。
import pandas as pd
import datetime,os,re
df = pd.DataFrame() # 设置中间变量,为空dataFrame数据体,用于存储新“总表.xlsx”数据或售卖数据
df0 = pd.DataFrame() # 设置中间变量,为空DataFrame数据体,用于存储原“总表.xlsx”数据
df1 = pd.DataFrame() # 设置中间变量,为空DataFrame数据体,用于存储原售卖的中间数据
df2 = pd.DataFrame() # 设置中间变量,为空DataFrame数据体,用于存储当次入库的中间数据
now = datetime.datetime.now().strftime(r'%m月%d日%H时%M') # "%H:%M"应该怎么加进去?
rW1 = ['入库时间','商品ID','商品名称','入库数量','商品价格','金额','说明备注']
rW2 = ['入库时间','商品ID','商品名称','库存数量','商品价格','金额','说明备注']
fpth = r'd:/CHRKGL' # 结尾不能用“\”,为了操作方便,也不能用“d:/CHRKGL/”
fnm = rf'总库.xlsx'
nfnm = f'/{now}入库.xlsx'
snfnm=f'/售卖{now}.xlsx'
if not os.path.exists(fpth): # 判断是否存在特定目录,不存在就创建
os.makedirs(fpth)
if fnm in os.listdir(fpth): # 把已经存在的库存文件数据交给df0,并设置好列标签。
df0 = pd.read_excel(fpth+'/'+fnm)# 加“header=1”只是便于观察,并不能建立列索引
else: # 这几句的目的是如果是第一次运行,没有“总库.xlsx”的话,就创建个DataFrame数据供调用
df3 = pd.DataFrame()# 其实可能没有这个必要。既然还没有,那新生成的就自己做主
df0 = pd.DataFrame(df3, columns=rW2)
# df0['原库存'] = df0['新库存'] # 处理中间数据
# df0['新库存'] = 0 # 处理中间数据
# df0['入库数量'] = 0 # 同上
def inpt(*x): # 收集需输入的内容
nW = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')#
iDn = int(rvw1(str(input('请扫描商品条码:').strip()))) # 自定义函数处理扫码,不足规定长度重新扫
nM = rvw2(str(input('请输入商品名称:').strip())) # 自定义函数去除空格和回车,限定输入长度
cNt = int(rvw3(rvw2(input('请输入入库数量:').strip()))) # 自定义函数判断是否为整数
pRc = rvw4(input('请输入商品价格:').strip().replace('。','.'))# 判断是否有浮点数,消除简单失误
aMnt = float(int(cNt) * pRc) # 计算本次入库物品金额,需转为浮点数
rMk = str(input('说明:')).strip() # 对输入内容无限制
if len(rMk) == 0:
rMk=czy
rW3 = # 将以上内容组成个列表
print('#'*50)
print(f'商品名称:{nM}')
print(f'入库数量:{cNt}')
print(f'商品价格:{pRc}')
print('#'*50)
j=1
while j: # 判断输入是否有误,有误的话删除此条并重新输入
hd = input('检查刚才输入是否有误,条目是否重复,数量、价格是否准确。\n'
'按“y”、“Y”或“是”重新输入,按回车等其它键继续输入下一条:')
if (len(hd) == 0) or (str(hd) not in ['y','Y','是']): # 回车空格等均表示输入正确,将继续下一条
return rW3
else:
nM = rvw2(str(input('请输入商品名称:').strip())) # 输入错误的只能是以下三项,未更新录入时间
cNt = int(rvw3(rvw2(input('请输入入库数量:').strip())))
pRc = rvw4(input('请输入商品价格:').strip().replace('。', '.')) # 简单排除“。”
aMnt = float(int(cNt) * pRc)
rW3 = [] # 疑问:能不能清空while循环外的那个“rW3”?
rW3 = # 将以上内容组成个列表。如上句未能清空,会不会
# 是否会造成while外的rW3列表意外扩充?
print('#'*50)
print(f'商品名称:{nM}')
print(f'入库数量:{cNt}')
print(f'商品价格:{pRc}')
print('#'*50)
def rvw1(x): # 判断扫码是否正确,正确扫码为13位数字,实验码3位数字
while len(x) != 3:
print('扫码失败。正确条码是行13位长的数字,\n'
'请重新扫码!如仍不成功,可手动输入。')
x = str(input('请扫描商品条码:').strip())
return x
def rvw2(x): # 判断空回车
while len(x) < 1:
print('此处不能为空!')
x = str(input('请重新输入:').strip())
return x
def rvw3(x): # 判断是否为整数
while not x.isnumeric():
print('入库数量应为整数,请敲回车并重新输入!')
x = rvw2(input('请输入入库数量:').strip())
return x
def rvw4(x): # 用正则判断是否为浮点数.如果不用正则,少加载个模块,程序应该小一些
while bool(re.search(r'\d', x)) != 1:
print('请输入整数或小数,注意小数点是英文半角的“.”,不是中文的“。”')
return rvw4(input('请重新输入商品价格:').strip().replace('。','.'))
flt = re.findall("(\d+)(\.*\d*)", x)
if len(flt) == 0:
return int(flt)
else:
return float(x)
def ipt(x): # 循环输入,每次一行,每行一个列表。
i = 1 # 设置循环变量,初始值为1
while i > 0:
# dT.append(inpt(x)) # 加不加x呢?如是多处调用此自字义函数,则应用x参数。
dT.append(inpt()) # 将通过自定义函数审核的输入内容逐次添加到二维列表中
# dT.insert(0,i) # 在每个列表元素(也是个列表)的头部插入序号,为变量i-1。用行标签也可以不用加序号了
eN = input('是否继续入库:').strip() # 过滤掉空格
if (len(eN) == 0): # 直接回车则继续
i += 1
elif str(eN) not in ['n', 'N', '不']: # 根据输入内容的第一个字符判断是否结束
i += 1
else:
i = 0
return dT
def cfcl(x): # 本次输入重复项的查找显示删除
if True in df2['商品ID'].duplicated(): # 是不是要把“df2”换成“X”?
key_duplicated = df2.duplicated(False)] # 对df2['商品ID']使用duplicated方法,
# 并获取False所对应的数据,最后把获取的数据交给key_duplicated变量,保存为DataFrame数据体。
print("'商品ID'列中输入有重复:")
print('#' * 50)
print(key_duplicated[['商品ID', '商品名称']]) # 只显示数据体key_duplicated中的两列
print('#' * 50)
print('本次入库中扫码录入的“商品ID”重复的商品将全部删除,\n可在本程序结束后再次运行,重新录入该商品。\n')
os.system('pause')
df2.drop_duplicates(subset='商品ID',keep=False,inplace=True) # keep的值只有"first", "last"
# 或False。注意subset的参数,是判断重复的列标签。
return df2
def dfchl(x): # “总库.xlsx”合表后的重复项的处理
df = x.fillna(0)# groupby需要轴对齐,"NaN"和“Null”不能参与运算,但0可以
df_dpl = df.duplicated(False)]# 对df['商品ID']使用duplicated方法,
# 并获取False所对应的数据,最后把获取的数据交给df_dpl变量,保存为DataFrame数据体。
# df.drop_duplicates(subset=['商品ID'], keep=False, inplace=True) # 从df中删除“商品ID”重复的各行
# print(df) # 如果用“inplace=True”参数,df中就已经只剩“商品ID”不重复的行了。
df_sgl = df.drop_duplicates(subset=['商品ID'], keep=False, inplace=False)# 不改df就需要另外的变量来保存结果
df_dpl_sgl = df_dpl.drop_duplicates(subset='商品ID', keep='first', inplace=False)# 不要随便为了省事就用True
df_dpl_sum = df_dpl.groupby('商品ID')['入库数量'].sum().reset_index()# "reset_index"和“reset_index()”结果不一样
df_dpl_sgl['入库数量'] = df_dpl_sum['入库数量']# 根据“商品ID”分组后,对其它数值列求和,如果指定字符串列,则扩充字符串
df = pd.concat(, axis=0, ignore_index=True)
return df
czy = rvw2(input('请输入操作人姓名或编号:').strip())
cz = input('售卖还是入库?售卖请直接回车;入库请输入"RK"后回车:').strip()
if cz == 'RK':
dT = [] # 设置用于保存输入内容的空列表
df2 = pd.DataFrame(ipt(dT), columns=rW1)
cfcl(df2)
df2 = df2 # 各列重新排序
df2.to_excel(fpth+'/'+nfnm) #这个读入路径和文件名的方式实在丑
print('#' * 50)
print(df2)
print('#' * 50)
print(f'本次输入商品如上显示,保存在"{fpth}"目录,文件名"{nfnm}"。')
os.system('pause')
# df2['原库存'] = 0 # 在df2中,均为新输入,df2['原库存']均视为0。如有和df0中的重复项,将专门调整
# df2['新库存'] = 0
# df2['库存金额'] = 0
df = pd.concat(, axis=0, ignore_index=True)
df = dfchl(df)
df['金额'] = df['商品价格'].astype(float) * df['入库数量'].astype(float) # df2 * df2['新库存']]
df['库存数量'] = df['入库数量']
# # # df0.to_excel('d:/Temp/食堂窗口售卖表.xlsx',index=False,columns=False)
# # # df0.to_excel('d:/Temp/食堂窗口售卖表.xlsx',columns=False) # columns=True也不行,没这参数
df = df # 不明白为啥df最后会多出几列,先用这个取巧的办法吧。
df.to_excel(fpth+'/'+fnm)
print('#' * 50)
print(df)
print('#' * 50)
os.system('pause')
print(f'本次入库结果如上显示,保存在"{fpth}"目录,文件名"{fnm}"\n,请打开该文件核对。')
else:
print('#' * 50)
print('开始售卖!')
print('#' * 50)
os.system('pause')
if fnm not in os.listdir(fpth):# 上面已经有过这句了,怎么利用一下而不是重复写一遍呢?
print('#' * 50)
print('"总库.xlsx"尚未建立,请先将商品入库再售卖。\n按任意键退出程序。')
print('#' * 50)
os.system('pause')
else:
df0 = pd.read_excel(fpth + '/' + fnm)# 读取“总库.xlsx”
print('#' * 50)
print(df0) # 显示商品总库存
print('#' * 50)
i = 1
stb = []
id01=[]
while i:
siDn = rvw2(input('请扫描商品条码:').strip())
if siDn == '0':# 输入0结束程序
i = 0
print('#' * 50)
print('要结束商品售卖吗?按任意键退出程序!')
print('#' * 50)
os.system('pause')
else:
if len(siDn) != 3:
print('#' * 50)
print('刚才扫码可能不正确。正确商品ID为13位连续阿拉伯数字,请重新扫码!')
print('#' * 50)
continue
i = i + 1
snW = datetime.datetime.now().strftime('%m-%d %H:%M:%S')#
id01=(df0['商品ID'] == int(siDn)).values.tolist()
if True not in id01:# 要先做判断,是否在“总库.xlsx”中及其库存数
print('#' * 50)
print('该商品可能还没入库,请结束程序,进行入库操作后再售卖!')
print('#' * 50)
break
snM = df0 == int(siDn)]['商品名称'].values.astype(str)
spRc = df0 == int(siDn)]['商品价格'].values.astype(float)
print('#' * 50)
print(f'应收款{spRc}元。')
print('#' * 50)
srMk = czy
srW3 =
stb.append(srW3)
id01=[]
df1 = pd.DataFrame(stb, columns=['售卖时间', '商品ID', '商品名称', '商品价格', '售卖计数', '说明备注'])
if df1.shape == 0:
print('#' * 50)
print('未入库的商品无法售卖!按任意键退出。')
print('#' * 50)
os.system('pause')
quit()
else:
if len(id01) == 0:
df = df1.groupby(['商品ID', '商品名称', '商品价格'])['售卖计数'].sum()# 这里如是多列要用双层的“[]”
df = df.reset_index()
df['商品金额'] = df['商品价格'] * df['售卖计数']
dfcnts = df[['商品ID','售卖计数']].copy()
dfcnts['商品ID'] = dfcnts['商品ID'].astype(str)
df0 = df0.copy()
df0['商品ID'] = df0['商品ID'].astype(str)
df00 = pd.merge(df0,dfcnts,on='商品ID',how='outer')
df00.fillna(0,inplace=True)
df00['库存数量']=df00['库存数量']-df00['售卖计数']
df00['金额']=df00['商品价格']*df00['库存数量']
df0 = df00
df.loc + 1, '商品金额'] = df['商品金额'].sum()
df.loc, '商品ID'] = '本次售卖总计'
df.to_excel(fpth + '/' + snfnm)
df0.to_excel(fpth + '/' + fnm)
print(f'本次售卖结果如上显示,保存在"{fpth}"目录,文件名"{snfnm}"\n,'
f'"{fnm}"的“库存数量”已根据本次售卖核减,请打开该文件核对。')
print(df0)
print('#' * 50)
print(df)
print('#' * 50)
else:
df = df1[:-1].groupby(['商品ID', '商品名称', '商品价格'])[['售卖计数']].sum()# 这里如是多列要用双层的“[]”
df = df.reset_index()
df['商品金额'] = df['商品价格'] * df['售卖计数']
dfcnts = df[['商品ID', '售卖计数']].copy()
dfcnts['商品ID'] = dfcnts['商品ID'].astype(str)
df0 = df0.copy()
df0['商品ID'] = df0['商品ID'].astype(str)
df00 = pd.merge(df0, dfcnts, on='商品ID', how='outer')
df00.fillna(0, inplace=True)
df00['库存数量'] = df00['库存数量'] - df00['售卖计数']
df00['金额'] = df00['商品价格'] * df00['库存数量']
df0 = df00
df.loc + 1, '商品金额'] = df['商品金额'].sum()
df.loc, '商品ID'] = '本次售卖总计'
df.to_excel(fpth + '/' + snfnm)
df0.to_excel(fpth + '/' + fnm)
print(f'本次售卖最后一项未在{fnm}中,请先将该商品入库,\n'
f'其它结果如下显示,保存在"{fpth}"目录,文件名"{snfnm}"\n'
f'"{fnm}"的“库存”已根据本次售卖核减,请打开该文件核对。')
print(df0)
print('#' * 50)
print(df)
print('#' * 50)
欢迎多多指出不足,不管是逻辑思路、语法技巧,必虚心接受!
做成单文件后,居然有65M之多,现在一个简版的Office2003,不过二三十M,Office2010也不过六七十M,,,唉编程之路啊 我建议你还是用excel做吧~ python在于大量数据自动化 你这又增加又输入的(没看你的1-xx条)、,类似库存系统,找个excel版的库存系统吧。 总算跑通了,{:1_899:},但是代码比较臃肿,应该可以精简...再看一下循环部分. 不明觉厉 虽然看不懂但是很厉害 看着比较臃肿的 加油,还有优化空间 zrwd01 发表于 2020-11-30 18:06
总算跑通了,,但是代码比较臃肿,应该可以精简...再看一下循环部分.
发现有个计算错了,不过不是主要问题,现在主要问题是做成文件后太大,65M! 你不配流程图啊
页:
[1]
2