Python Django制作一个简单的超市收银系统,基本设计思路是:收银系统打开的时候条码框自动获得焦点,用条码枪输入提前录入好的商品,并在页面显示商品信息,如果多次扫描同一条码,数量和金额会增加。"收款金额"输入金额回车后获得找零金额,点击保存按钮,将此次的收款数据存入数据库。"取消"按钮会清空当前收银数据,刷新页面、刷新流水单号。
设计环境
# 超市收银软件设计环境
Python:3.9.0
Django:3.1.3
Mysql:8.0.22
Pycharm:2020.2
创建项目后新建一个应用sale(收银功能)
数据模型
# 收银数据表 models.py
from django.db import models
class Category(models.Model):
"""商品分类"""
category_name = models.CharField('分类',max_length=32)
decription = models.CharField('描述',max_length=200)
class Meta:
verbose_name = '商品分类'
verbose_name_plural = '分类'
def __str__(self):
return self.category_name
class Product(models.Model):
"""商品数据模型"""
product_id = models.CharField('商品编码',max_length=128)
barcode = models.CharField('条形码',max_length=128)
product_name = models.CharField('商品名称',max_length=200)
unit = models.CharField('单位',max_length=32)
unit_price = models.DecimalField(verbose_name='单价',max_digits=8,decimal_places=2)
category = models.ForeignKey(Category,verbose_name='分类',on_delete=models.CASCADE,default=1)
inventory = models.CharField('库存',max_length=64)
class Meta:
verbose_name = '商品'
verbose_name_plural = '商品'
def __str__(self):
return self.product_name
class SaleList(models.Model):
"""收银数据模型"""
order_code = models.CharField('订单编号',max_length=128)
total_number = models.CharField('数量',max_length=32)
total_price = models.CharField('总金额',max_length=128)
receive_money = models.DecimalField('收款金额',max_digits=8,decimal_places=2)
return_money = models.DecimalField('找零金额',max_digits=8,decimal_places=2)
class Meta:
verbose_name = verbose_name_plural = '收银数据'
def __str__(self):
return self.order_code
class SaleDetail(models.Model):
"""收银详细数据"""
detail_number = models.CharField('订单编号',max_length=128)
product_id = models.CharField('商品编号',max_length=128)
product_name = models.CharField('商品名称', max_length=200)
unit = models.CharField('单位', max_length=32)
unit_price = models.DecimalField(verbose_name='单价', max_digits=8, decimal_places=2)
number = models.CharField('数量',max_length=32)
money = models.DecimalField('合计',max_digits=8,decimal_places=2)
class Meta:
verbose_name = '收银明细'
verbose_name_plural = '收银明细'
def __str__(self):
return self.detail_number
由于只是一个演示案例,并没有设计商品管理功能,录入商品有两种方式,数据库操作和Django自带的Admin后台。
业务代码
在sale目录下创建一个myclass文件夹,并新建sale_id.py(流水单号)、shop_sale.py(收银功能)两个模块,用来具体实现业务功能。
# 自动流水单号 sale_id.py
from datetime import datetime
import random
# 根据当前时间随机生成流水单号
class OrderCode:
# 构造函数
def __init__(self):
self.serialnum = '' # 存储生成的单号,默认为空
# 自动执行函数
self.get_serialnum()
def get_serialnum(self):
"""生成订单号"""
# 获取当前系统时间
dt = datetime.now()
# 生成订单号
self.serialnum = '%04d%02d%02d%02d%02d%02d' % (dt.year,dt.month,dt.day,dt.hour,dt.minute,dt.second)
# 生成4位随机数字
ran_list = random.randint(0,9999)
# 随机数附加到尾部
self.serialnum += '%04d' % (ran_list)
if __name__ == '__main__':
obj = OrderCode()
print(obj.serialnum)
# 收银功能的具体代码 shop_sale.py
from sale.models import Product,SaleList,SaleDetail
from decimal import Decimal
class Customer:
# 构造函数
def __init__(self):
self.buy_list = [] # 储存当前扫描的商品
self.total_number = 0 # 储存收银商品总数
self.total_money = 0.0 # 存储收银总金额
self.receive_money = 0.0 # 存储收款金额
self.return_money = 0.0 # 存储找零金额
def get_product(self,barcode):
"""收银模块:通过条形码查找商品"""
if barcode is not None:
try:
product = Product.objects.filter(barcode=barcode).first()
# 添加商品到收银列表
self.add_product_buylist(product)
except Exception as e:
self.error_info = "商品扫描失败:" + str(e)
def add_product_buylist(self,product):
"""收银模块:添加商品到收银列表"""
temp_list = {
'product_id':product.product_id,
'product_barcode':product.barcode,
'product_name':product.product_name,
'unit':product.unit,
'unit_price':product.unit_price,
'money':product.unit_price,
'number':1,
}
# 添加到当前的buy_list
if len(self.buy_list) == 0:
self.buy_list.append(temp_list)
else:
# 遍历当前的buy_list
for index in range(len(self.buy_list)):
if temp_list['product_id'] == self.buy_list[index]['product_id']:
self.buy_list[index]['number'] +=1
self.buy_list[index]['money'] = self.buy_list[index]['unit_price'] * self.buy_list[index]['number']
break
if index == len(self.buy_list) -1:
self.buy_list.append(temp_list)
def get_total_info(self):
"""收银模块:商品总数和总金额"""
self.total_number = 0
self.total_money = Decimal(0.0)
# 遍历收银的商品列表
for product in self.buy_list:
self.total_number +=product['number']
self.total_money +=product['money']
# 保留金额的两位小数
self.total_money = round(self.total_money,2)
def get_receive_return_money(self, receive):
"""收银模块:收款金额和找零"""
if receive is not None:
self.receive_money = Decimal(receive)
self.return_money = self.receive_money - self.total_money
else:
pass
def delete_product_buylist(self,barcode):
"""收银模块:删除列表中的商品"""
# 先遍历buy_list
for index in range(len(self.buy_list)):
# 判断传递过来的id是否在buy_list中,这里中括号中的product_barcode要和上面buy_list中的一样
if int(self.buy_list[index]['product_barcode']) == barcode:
self.buy_list.pop(index)
break
else:
break
def submit_sale_buylist(self,serialnum):
"""收银模块:保存到数据库"""
# 保存每个收银单的总数据
if self.buy_list is not None:
try:
sql_temp = SaleList.objects.create(order_code=serialnum,total_number=self.total_number,
total_price=self.total_money,receive_money=self.receive_money,
return_money=self.return_money)
except Exception as e:
self.error_info = '收银数据保存失败:' + str(e)
# 遍历收银列表,把收银详细数据存储到SaleDetail表中
for product in self.buy_list:
try:
buy_sql = SaleDetail.objects.create(detail_number=serialnum,product_id=product['product_id'],
product_name=product['product_name'],unit=product['unit'],
unit_price=product['unit_price'],number=product['number'],
money=product['money'])
except Exception as e:
self.error_info = '收银详细数据出错:' + str(e)
# views.py
from django.shortcuts import render,redirect
from sale.myclass import shop_sale,sale_id # 引用自定义模块
# 实例化收银模块
sale_customer = shop_sale.Customer()
# 实例化自动单号
sale_time = sale_id.OrderCode()
def sale(request):
"""收银模块:显示"""
if len(sale_time.serialnum) == 0:
sale_time.serialnum()
# 统计总数量和金额
sale_customer.get_total_info()
context = {
'sale_id':sale_time.serialnum,
'sale_customer':sale_customer,
}
return render(request,'sale/sale.html',context)
def add_sale(request):
"""收银模块:添加商品到收银列表"""
# 获取当前的条形码
barcode = request.GET.get('barcode')
# 添加到buy_list
sale_customer.get_product(barcode)
return redirect('/sale/')
def get_return_money(request):
"""收银模块:收款金额和找零"""
receive = request.GET.get('receive')
if receive is not None:
sale_customer.get_receive_return_money(receive)
else:
return redirect('/sale/')
return redirect('/sale/')
def delete_product(request,barcode):
"""收银模块:删除商品"""
sale_customer.delete_product_buylist(barcode)
return redirect('/sale/')
def cancel_sale(request):
"""收银模块:取消订单"""
sale_customer.buy_list.clear() # 清空当前商品列表
sale_id.serialnum = '' # 清空当前单号
sale_customer.total_number = 0 # 清空商品数量
sale_customer.total_money = 0.0 # 清空总金额
sale_customer.receive_money = 0.0 # 清空收款金额
sale_customer.return_money = 0.0 # 清空找零金额
return redirect('/sale/')
def submit_sale(request):
"""收银模块:提交到数据库"""
sale_customer.submit_sale_buylist(sale_time.serialnum)
sale_customer.buy_list.clear() # 清空当前商品列表
sale_id.serialnum = '' # 清空当前单号
sale_customer.total_number = 0 # 清空商品数量
sale_customer.total_money = 0.0 # 清空总金额
sale_customer.receive_money = 0.0 # 清空收款金额
sale_customer.return_money = 0.0 # 清空找零金额
return redirect('/sale/')
URL路由设置
# urls路由设置
from django.urls import path
from sale.views import sale,add_sale,get_return_money,delete_product,cancel_sale,submit_sale
urlpatterns = [
path('',sale,name='sale'), # 收银模块
path('add_sale/',add_sale,name='add_sale'), # 添加收银商品
path('get_return/',get_return_money,name='get_return'), # 计算收款金额
path('del_sale/<int:barcode>',delete_product,name='del_sale'), # 删除收银商品
path('cancel_sale/',cancel_sale,name='cancel_sale'), # 取消当前订单
path('submit_sale/',submit_sale,name='submit_sale'), # 保存订单
]
HTML模版文件
# 收银系统前端模版 sale.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>收银系统</title>
<link type="text/css" rel="stylesheet" href="/images/css/common.css">
<script src="/images/js/jquery.js"></script>
</head>
<body>
<header>
<div class="logo"><img src="/images/logo.png"></div>
</header>
<main>
<table id="product_table">
<thead>
<tr>
<th>序号</th>
<th>商品编码</th>
<th>商品名称</th>
<th>单价</th>
<th>数量</th>
<th>单位</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for list in sale_customer.buy_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ list.product_barcode }}</td>
<td>{{ list.product_name }}</td>
<td>{{ list.unit_price }}</td>
<td>{{ list.number }}</td>
<td>{{ list.unit }}</td>
<td>{{ list.money }}</td>
<td><a href="{% url 'del_sale' list.product_barcode %}">删除</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</main>
<footer>
<div class="code_info">
<span>操作人:<font>{{ request.session.nickname }}</font></span>
<span>流水单号:<font>{{ sale_id }}</font></span>
</div>
<form>
<p>
<input type="text" id="barcode" name="barcode" placeholder="请扫描条形码" autofocus="autofocus" autocomplete="off">
<input type="text" id="receive" name="receive" placeholder="收款金额" autocomplete="off">
</p>
<p>
<span id="total_num">总数量:<font>{{ sale_customer.total_number }}</font></span>
<span id="total_money">总金额:<font>{{ sale_customer.total_money }}</font></span>
<span id="return">找零:<font>{{ sale_customer.return_money }}</font></span>
</p>
<div class="btn">
<a href="{% url 'submit_sale' %}" class="save">保存</a>
<a href="{% url 'cancel_sale' %}" class="cancel">取消</a>
</div>
</form>
</footer>
<script>
$(function() {
$('#barcode').bind('keydown', function(event) {
var event = window.event || arguments.callee.caller.arguments[0];
if (event.keyCode == 13) {
// 获取商品条形码
barcode = $("#barcode").val();
location.href = "{% url 'add_sale' %}?barcode=" + barcode;
}
});
$('#receive').bind('keydown', function(event) {
var event = window.event || arguments.callee.caller.arguments[0];
if (event.keyCode == 13) {
// 获取商品条形码
receive = $("#receive").val();
if (receive == null || receive === '') {
location.href = "{% url 'sale' %}";
} else {
location.href = "{% url 'get_return' %}?receive=" + receive;
}
}
});
});
</script>
</body>
</html>
以上就是一个基本的收银软件,可以根据自己的需求对功能进行完善。结构很简单,知识点其实是将收银数据临时放到字典和列表中,当激活保存业务时,收银数据才会储存到数据库。