news 2026/9/16 14:53:31

Django进销存系统:采购入库销售出库库存扣减成本核算

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Django进销存系统:采购入库销售出库库存扣减成本核算

简介:这是一套基于Python Django框架开发的商品销售进销存系统完整源码包,面向计算机专业本科生、毕业设计学生及初阶Web开发学习者,解决小型商贸场景下的商品入库、销售、库存查询与基础报表等核心业务管理需求。资源共2000个文件,以1623个JavaScript前端交互脚本、261个HTML页面模板和51个CSS样式文件为主体,辅以JSON配置、XML配置及少量Python后端逻辑文件,整体结构体现典型Django前后端分离雏形,压缩包仅5.29MB,轻量易部署。已有72人下载学习,适合作为期末课程设计、毕业设计参考或Django全栈开发入门实践项目。资源包含可运行的完整数据库(SQLite)、详细文档说明及调试通过的源代码,预览可见bootstrap、font-awesome、datetimepicker等主流前端组件集成,便于理解电商类系统UI构建与数据联动逻辑。

1. 这不是又一个“Django博客模板”:它是一套能直接跑通采购入库、销售出库、库存实时扣减、多仓库调拨和成本核算的Python进销存系统

你手头拿到的这个.zip包,表面看是「Python基于Django商品销售进销存系统源代码+文档说明+数据库」,但实际价值远超字面——它不是教学Demo,也不是仅含CRUD的骨架项目,而是一套已通过真实小商户账期验证的业务闭环系统。核心模块包括:商品多规格管理(如颜色/尺码/批次)、供应商与客户分级档案、采购订单→入库单→应付账款联动、销售订单→出库单→应收账款联动、库存流水按时间轴可追溯、加权平均法自动计算销售成本、期末库存余额自动结转。它不依赖Vue或React前端,纯Django Admin定制化实现业务操作流,所有单据状态变更均触发库存数量与金额双维度校验。适合中小批发商、电商仓配团队、校园超市等需快速落地、拒绝复杂部署、且对数据一致性有硬性要求的场景。如果你正为毕业设计找可运行的数据库课程设计选题,或需要一套能嵌入现有IT流程的轻量级进销存底座,这个包里的models.pyviews.py就是起点。

2. 从解压到本地运行:用Django 4.2+Python 3.10复现完整进销存业务流

2.1 环境准备:为什么必须锁定Python 3.10与Django 4.2

该系统在requirements.txt中明确声明依赖Django>=4.2,<4.3python>=3.10。原因在于其库存扣减逻辑使用了F()表达式结合select_for_update()的原子锁机制,而 Django 4.2 是首个将select_for_update(nowait=True)默认行为与 PostgreSQL/MySQL 8.0+ 兼容性完全对齐的版本;同时,decimal_places=2DecimalField中的精度控制在 Python 3.10 的decimal模块中才彻底规避了浮点舍入误差。若强行使用 Python 3.9 或 Django 4.1,会在「销售出库时库存不足却未抛异常」或「成本计算出现0.01元偏差」等关键节点失效。

提示:不要用pip install django直接安装最新版。执行以下命令精准构建环境:

python -m venv venv_instock source venv_instock/bin/activate # Windows用 venv_instock\Scripts\activate pip install -U pip pip install "Django==4.2.13" "django-filter==23.3" "python-decouple==3.8"

2.2 数据库初始化:用SQLite快速验证,再平滑迁移到MySQL

系统默认配置为 SQLite(见settings.pyDATABASES),这是为降低入门门槛。但真实业务必须切换至 MySQL——因为库存并发扣减需行级锁支持,而 SQLite 的 WAL 模式在高并发写入下易触发database is locked错误。

2.2.1 先用SQLite跑通全流程(5分钟验证)

解压后进入项目根目录,执行:

python manage.py migrate python manage.py createsuperuser # 创建管理员账号 python manage.py loaddata fixtures/initial_data.json # 加载预置商品、供应商等基础数据 python manage.py runserver

访问http://127.0.0.1:8000/admin/,用刚创建的账号登录。你会看到已预置的「商品管理」「采购单」「销售单」「库存查询」等菜单。此时尝试新建一条采购单(选择商品、填写数量、单价),保存后立即查看「库存查询」页面——数量应实时增加。这证明模型关系、信号触发、库存更新逻辑全部就绪。

2.2.2 切换至MySQL并导入初始数据

修改settings.pyDATABASES配置:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'instock_db', 'USER': 'instock_user', 'PASSWORD': 'your_secure_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", 'charset': 'utf8mb4', }, 'TEST': { 'CHARSET': 'utf8mb4', 'COLLATION': 'utf8mb4_unicode_ci', } } }

在 MySQL 中执行建库与授权:

CREATE DATABASE instock_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'instock_user'@'localhost' IDENTIFIED BY 'your_secure_password'; GRANT ALL PRIVILEGES ON instock_db.* TO 'instock_user'@'localhost'; FLUSH PRIVILEGES;

然后执行迁移:

python manage.py makemigrations --empty instock # 清空可能残留的迁移文件 python manage.py migrate python manage.py loaddata fixtures/initial_data.json

2.3 关键模型关系解析:为什么采购单与库存变动必须解耦

系统未采用「采购单保存即更新库存」的直连模式,而是通过PurchaseOrderPurchaseItemStockMovement三级关联。StockMovement模型独立存在,字段包括movement_type('IN'/'OUT'/'ADJUST')、related_id(外键到采购单/销售单ID)、quantity_changecost_amount。这种设计带来三个实际收益:

  1. 审计合规:所有库存变动均有独立记录,可精确追溯某笔库存增加源于哪张采购单的第几行商品;
  2. 状态可控:采购单可处于「草稿」「已审核」「已入库」状态,仅当状态为「已入库」时,才触发StockMovement创建;
  3. 成本分离PurchaseItem存储采购价,StockMovement存储加权平均后的实际入库成本,避免因后续调价污染历史成本。

查看models.pyPurchaseOrder.save()方法,你会发现它只做状态校验,真正的库存更新由post_save信号处理器create_stock_movement_on_purchase_complete完成——这是Django进销存系统区别于普通CRUD项目的分水岭。

3. 核心业务功能落地:采购入库、销售出库、库存预警与成本核算四步实操

3.1 采购入库:从订单创建到库存增加的完整链路

3.1.1 创建采购订单并审核

登录 Admin 后,进入「采购管理」→「采购订单」,点击「添加采购订单」:

  • 供应商:选择预置的「广州电子配件有限公司」
  • 订单日期:设为今天
  • 状态:保持默认「草稿」
  • 在下方「采购明细」中添加商品:选择「USB-C数据线(黑色)」,数量填100,单价填12.50

保存后,返回列表页,找到该订单,点击右侧「审核」按钮(系统自定义Action)。此时订单状态变为「已审核」,但库存尚未增加。

3.1.2 执行入库操作并验证库存

点击该订单右侧「执行入库」链接(此为Admin自定义视图,路径为/admin/instock/purchaseorder/<id>/receive/)。页面显示待入库明细,确认无误后提交。此时系统执行:

  1. 创建StockMovement记录,movement_type='IN'quantity_change=100cost_amount=1250.00
  2. 更新ProductStock表中对应商品的quantity字段
  3. 将采购单状态更新为「已入库」

验证方式:进入「库存管理」→「商品库存查询」,搜索「USB-C数据线」,当前库存应显示100

注意:若在此步骤中手动修改ProductStock.quantity字段,系统会因StockMovement未生成而丢失审计线索。所有库存变更必须经由业务单据驱动。

3.2 销售出库:带库存校验与成本结转的原子操作

3.2.1 创建销售订单并检查库存可用性

进入「销售管理」→「销售订单」,添加新订单:

  • 客户:「深圳科技体验店」
  • 商品:「USB-C数据线(黑色)」,数量30
  • 提交前,系统在SalesOrder.clean()方法中调用check_stock_availability(),若当前库存<30则抛出ValidationError并阻止保存。这是防止超卖的第一道防线。
3.2.2 出库操作与成本自动核算

审核销售订单后,点击「执行出库」。系统执行:

  1. 创建StockMovement记录,movement_type='OUT'quantity_change=-30
  2. 查询该商品最近3次入库的StockMovement记录,按加权平均法计算本次出库成本:
    # 伪代码:实际在 stock/utils.py 中实现 total_cost = sum(m.cost_amount for m in recent_in_movements) total_qty = sum(m.quantity_change for m in recent_in_movements) avg_cost = total_cost / total_qty out_cost = avg_cost * 30
  3. 创建CostOfGoodsSold记录,记录out_cost值,用于利润表生成

验证:查看「库存查询」,该商品库存应变为70;进入「财务报表」→「销售成本明细」,可见新增一笔30 × 12.50 = 375.00的成本记录。

3.3 库存预警:基于安全库存与动态周转率的阈值配置

系统未使用静态阈值,而是提供两种预警模式:

预警类型配置位置触发逻辑示例
安全库存预警Product模型的safety_stock字段current_quantity <= safety_stock时,在Admin首页显示红色告警safety_stock=20,当前库存=18 → 告警
周转率预警InventoryReportView中的get_turnover_rate()若近30天销量/当前库存 < 0.5,标记为「滞销」30天销量=10,当前库存=50 → 周转率=0.2

在 Admin 中编辑任意商品,设置safety_stock=5,然后手动将库存调至4(通过「库存调整」功能),刷新首页即可看到「库存低于安全值」提示栏。

3.4 期末成本核算:一键生成加权平均单价与库存余额

进入「财务报表」→「期末库存盘点」,点击「生成本期结存」。系统执行:

  1. 对每个商品,聚合所有IN类型StockMovementcost_amountquantity_change
  2. 计算加权平均单价:sum(cost_amount) / sum(quantity_change)
  3. 用该单价 × 当前库存数量,得出「期末库存余额」
  4. 将结果写入InventoryValuation模型,供资产负债表调用

该过程在management/commands/calculate_inventory_valuation.py中实现,支持命令行批量执行:

python manage.py calculate_inventory_valuation --period "2024-Q2"

4. 生产部署关键参数:Nginx反向代理、Gunicorn进程管理与数据库连接池优化

4.1 Gunicorn配置:为什么必须启用preload与max-requests

gunicorn.conf.py中的关键参数如下:

command = '/path/to/venv/bin/gunicorn' args = [ 'instock.wsgi:application', '--bind', '127.0.0.1:8001', '--workers', '3', # CPU核心数×2,非盲目堆砌 '--worker-class', 'sync', '--preload', # 必须开启!避免每个worker重复加载Django模型导致内存泄漏 '--max-requests', '1000', # 每处理1000个请求重启worker,防止长连接内存累积 '--timeout', '120', '--keep-alive', '5', ]

--preload是进销存系统的生死线。若关闭,3个worker会各自加载一次models.py,而StockMovementpost_save信号会被注册3次,导致同一笔采购入库触发3次库存更新,造成数据错乱。

4.2 Nginx反向代理配置:静态文件分离与超时保护

/etc/nginx/sites-available/instock配置要点:

upstream django_app { server 127.0.0.1:8001; } server { listen 80; server_name instock.example.com; # 静态文件由Nginx直接服务,不经过Django location /static/ { alias /path/to/project/staticfiles/; expires 1y; add_header Cache-Control "public, immutable"; } # 关键:销售出库等耗时操作需延长超时 location /admin/instock/salesorder/ { proxy_read_timeout 300; # 默认60秒不够,出库需校验库存+计算成本+生成凭证 proxy_pass http://django_app; } location / { proxy_pass http://django_app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }

4.3 MySQL连接池:用mysqlclient的MAX_CONNS参数防雪崩

settings.pyDATABASES配置中追加:

'OPTIONS': { 'MAX_CONNS': 20, # 连接池最大连接数 'MIN_CONNS': 5, # 最小空闲连接数 'CONN_MAX_AGE': 0, # 禁用持久连接,避免长事务阻塞 }

理由:进销存系统在「月结」期间会并发执行数百个StockMovement查询,若不设连接池上限,MySQL 的max_connections被打满后,新请求将直接失败。CONN_MAX_AGE=0是刻意为之——库存操作必须短平快,长连接反而增加死锁概率。

5. 进阶技巧:用Django Admin Action批量处理库存调整与自定义报表导出

5.1 批量库存调整:绕过单据流的紧急修正方案

当发现扫码错误导致某商品多录入50件时,无需走采购退单流程。在「商品库存查询」列表页:

  1. 勾选该商品所在行
  2. 下拉选择「批量库存调整」Action
  3. 输入调整量-50,填写调整原因「扫码重复录入」
  4. 提交

系统执行admin_actions/bulk_stock_adjustment.py中的逻辑:

  • 创建StockMovement记录,movement_type='ADJUST'
  • 不关联任何业务单据(related_id=None
  • 记录操作人与时间戳
  • 发送站内通知给财务组

此操作留痕、可追溯、不影响历史成本,是生产环境必备的救火能力。

5.2 自定义报表导出:用pandas生成带格式的Excel进销存汇总表

系统内置export_inventory_report管理命令,但常需定制字段。以导出「近7天各商品出入库汇总」为例,在management/commands/export_custom_report.py中编写:

import pandas as pd from django.core.management.base import BaseCommand from instock.models import StockMovement class Command(BaseCommand): def handle(self, *args, **options): qs = StockMovement.objects.filter( created_at__gte=timezone.now() - timedelta(days=7) ).values('product__name', 'movement_type').annotate( total_qty=Sum('quantity_change'), total_amount=Sum('cost_amount') ) df = pd.DataFrame(list(qs)) # 添加中文列名与条件格式 df.columns = ['商品名称', '变动类型', '数量合计', '金额合计'] df['变动类型'] = df['变动类型'].map({'IN': '入库', 'OUT': '出库', 'ADJUST': '调整'}) # 导出为Excel,冻结首行 with pd.ExcelWriter('7day_movement.xlsx', engine='openpyxl') as writer: df.to_excel(writer, index=False, sheet_name='7日汇总') worksheet = writer.sheets['7日汇总'] worksheet.freeze_panes = 'A2' self.stdout.write('报表已生成:7day_movement.xlsx')

执行python manage.py export_custom_report即可获得带格式的Excel,财务人员可直接用于对账。

5.3 数据库课程设计加分项:在Admin中嵌入库存流水时间轴可视化

利用django-admin-interfacecustom_css功能,在admin/base_site.html中注入轻量级时间轴JS:

{% block extrahead %} <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <style> .time-axis-chart { height: 200px; } </style> {% endblock %}

然后在admin/productstock.py中重写change_view,添加:

def change_view(self, request, object_id, form_url='', extra_context=None): extra_context = extra_context or {} movements = StockMovement.objects.filter( product_id=object_id ).order_by('-created_at')[:30] extra_context['movements_chart_data'] = json.dumps({ 'labels': [m.created_at.strftime('%m/%d %H:%M') for m in movements], 'data': [m.quantity_change for m in movements], }) return super().change_view(request, object_id, form_url, extra_context)

配合前端Chart.js渲染,让库存变动趋势一目了然——这在数据库课程设计答辩中,比纯文字描述「实现了库存管理」更具说服力。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 14:48:00

OpenWhispr原生Helper编译指南:13个平台专用二进制的构建全流程

OpenWhispr原生Helper编译指南&#xff1a;13个平台专用二进制的构建全流程 【免费下载链接】openwhispr Voice-to-text dictation app with local (Nvidia Parakeet/Whisper) and cloud models (BYOK). Privacy-first and available cross-platform. 项目地址: https://gitc…

作者头像 李华
网站建设 2026/9/16 14:46:59

2026职场趋势:AI与新能源成黄金赛道

1. 职场趋势的周期性波动解析每年三四月份的职场招聘高峰被称为"金三银四"&#xff0c;这已经成为职场人士的普遍认知。但2026年的就业市场却呈现出与往年截然不同的景象——传统热门行业的招聘需求明显降温&#xff0c;而一些新兴领域却逆势爆发。这种结构性变化背后…

作者头像 李华
网站建设 2026/9/16 14:41:00

STM32电子血压计实现:示波法、脉搏波提取与ADC采样全解析

简介&#xff1a;基于STM32设计的电子血压计完整项目包&#xff0c;面向嵌入式方向的学生与开发者&#xff0c;适用于课程设计、毕业设计、工程实训及学科竞赛等场景。资源已经过严格测试可直接运行&#xff0c;包含完整源码、工程文件及使用说明&#xff0c;可帮助快速复刻血压…

作者头像 李华