ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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.py和views.py就是起点。2. 从解压到本地运行用Django 4.2Python 3.10复现完整进销存业务流2.1 环境准备为什么必须锁定Python 3.10与Django 4.2该系统在requirements.txt中明确声明依赖Django4.2,4.3与python3.10。原因在于其库存扣减逻辑使用了F()表达式结合select_for_update()的原子锁机制而 Django 4.2 是首个将select_for_update(nowaitTrue)默认行为与 PostgreSQL/MySQL 8.0 兼容性完全对齐的版本同时decimal_places2在DecimalField中的精度控制在 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 Django4.2.13 django-filter23.3 python-decouple3.82.2 数据库初始化用SQLite快速验证再平滑迁移到MySQL系统默认配置为 SQLite见settings.py中DATABASES这是为降低入门门槛。但真实业务必须切换至 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.py中DATABASES配置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_modeSTRICT_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_userlocalhost IDENTIFIED BY your_secure_password; GRANT ALL PRIVILEGES ON instock_db.* TO instock_userlocalhost; FLUSH PRIVILEGES;然后执行迁移python manage.py makemigrations --empty instock # 清空可能残留的迁移文件 python manage.py migrate python manage.py loaddata fixtures/initial_data.json2.3 关键模型关系解析为什么采购单与库存变动必须解耦系统未采用「采购单保存即更新库存」的直连模式而是通过PurchaseOrder→PurchaseItem→StockMovement三级关联。StockMovement模型独立存在字段包括movement_typeIN/OUT/ADJUST、related_id外键到采购单/销售单ID、quantity_change、cost_amount。这种设计带来三个实际收益审计合规所有库存变动均有独立记录可精确追溯某笔库存增加源于哪张采购单的第几行商品状态可控采购单可处于「草稿」「已审核」「已入库」状态仅当状态为「已入库」时才触发StockMovement创建成本分离PurchaseItem存储采购价StockMovement存储加权平均后的实际入库成本避免因后续调价污染历史成本。查看models.py中PurchaseOrder.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/。页面显示待入库明细确认无误后提交。此时系统执行创建StockMovement记录movement_typeINquantity_change100cost_amount1250.00更新ProductStock表中对应商品的quantity字段将采购单状态更新为「已入库」验证方式进入「库存管理」→「商品库存查询」搜索「USB-C数据线」当前库存应显示100。注意若在此步骤中手动修改ProductStock.quantity字段系统会因StockMovement未生成而丢失审计线索。所有库存变更必须经由业务单据驱动。3.2 销售出库带库存校验与成本结转的原子操作3.2.1 创建销售订单并检查库存可用性进入「销售管理」→「销售订单」添加新订单客户「深圳科技体验店」商品「USB-C数据线黑色」数量30提交前系统在SalesOrder.clean()方法中调用check_stock_availability()若当前库存30则抛出ValidationError并阻止保存。这是防止超卖的第一道防线。3.2.2 出库操作与成本自动核算审核销售订单后点击「执行出库」。系统执行创建StockMovement记录movement_typeOUTquantity_change-30查询该商品最近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创建CostOfGoodsSold记录记录out_cost值用于利润表生成验证查看「库存查询」该商品库存应变为70进入「财务报表」→「销售成本明细」可见新增一笔30 × 12.50 375.00的成本记录。3.3 库存预警基于安全库存与动态周转率的阈值配置系统未使用静态阈值而是提供两种预警模式预警类型配置位置触发逻辑示例安全库存预警Product模型的safety_stock字段当current_quantity safety_stock时在Admin首页显示红色告警safety_stock20当前库存18 → 告警周转率预警InventoryReportView中的get_turnover_rate()若近30天销量/当前库存 0.5标记为「滞销」30天销量10当前库存50 → 周转率0.2在 Admin 中编辑任意商品设置safety_stock5然后手动将库存调至4通过「库存调整」功能刷新首页即可看到「库存低于安全值」提示栏。3.4 期末成本核算一键生成加权平均单价与库存余额进入「财务报表」→「期末库存盘点」点击「生成本期结存」。系统执行对每个商品聚合所有IN类型StockMovement的cost_amount与quantity_change计算加权平均单价sum(cost_amount) / sum(quantity_change)用该单价 × 当前库存数量得出「期末库存余额」将结果写入InventoryValuation模型供资产负债表调用该过程在management/commands/calculate_inventory_valuation.py中实现支持命令行批量执行python manage.py calculate_inventory_valuation --period 2024-Q24. 生产部署关键参数Nginx反向代理、Gunicorn进程管理与数据库连接池优化4.1 Gunicorn配置为什么必须启用preload与max-requestsgunicorn.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而StockMovement的post_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.py的DATABASES配置中追加OPTIONS: { MAX_CONNS: 20, # 连接池最大连接数 MIN_CONNS: 5, # 最小空闲连接数 CONN_MAX_AGE: 0, # 禁用持久连接避免长事务阻塞 }理由进销存系统在「月结」期间会并发执行数百个StockMovement查询若不设连接池上限MySQL 的max_connections被打满后新请求将直接失败。CONN_MAX_AGE0是刻意为之——库存操作必须短平快长连接反而增加死锁概率。5. 进阶技巧用Django Admin Action批量处理库存调整与自定义报表导出5.1 批量库存调整绕过单据流的紧急修正方案当发现扫码错误导致某商品多录入50件时无需走采购退单流程。在「商品库存查询」列表页勾选该商品所在行下拉选择「批量库存调整」Action输入调整量-50填写调整原因「扫码重复录入」提交系统执行admin_actions/bulk_stock_adjustment.py中的逻辑创建StockMovement记录movement_typeADJUST不关联任何业务单据related_idNone记录操作人与时间戳发送站内通知给财务组此操作留痕、可追溯、不影响历史成本是生产环境必备的救火能力。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__gtetimezone.now() - timedelta(days7) ).values(product__name, movement_type).annotate( total_qtySum(quantity_change), total_amountSum(cost_amount) ) df pd.DataFrame(list(qs)) # 添加中文列名与条件格式 df.columns [商品名称, 变动类型, 数量合计, 金额合计] df[变动类型] df[变动类型].map({IN: 入库, OUT: 出库, ADJUST: 调整}) # 导出为Excel冻结首行 with pd.ExcelWriter(7day_movement.xlsx, engineopenpyxl) as writer: df.to_excel(writer, indexFalse, sheet_name7日汇总) 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-interface的custom_css功能在admin/base_site.html中注入轻量级时间轴JS{% block extrahead %} script srchttps://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_contextNone): extra_context extra_context or {} movements StockMovement.objects.filter( product_idobject_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渲染让库存变动趋势一目了然——这在数据库课程设计答辩中比纯文字描述「实现了库存管理」更具说服力。本文还有配套的精品资源点击获取
返回列表