# tb3 **Repository Path**: techbrew/tb3 ## Basic Information - **Project Name**: tb3 - **Description**: 基于 Django 框架开发的酒厂生产管理系统。 - **Primary Language**: Python - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-05 - **Last Updated**: 2024-12-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README TechBrew 第三代 [TOC] ## 1. 项目介绍 ### 1.1. 介绍 项目后端基于 Django 3.0 进行开发。服务采用 Nginx 代理静态文件,可以参考 nginx.conf 进行配置,Django 服务使用 uwsgi 进行启动,基于 tb3_uwsgi.ini 配置文件。 项目目录 ``` ├────.gitattributes ├────.gitignore ├────data_import.py ├────docker_build_base.sh ├────docker_build_product.sh ├────dockerfile_base ├────dockerfile_product ├────manage.py ├────nginx.conf ├────oa/ │ ├────__init__.py │ ├────admin.py │ ├────apps.py │ ├────migrations/ │ │ ├────0001_initial.py │ │ ├────0002_packagegroup_reference_cost.py │ │ └────__init__.py │ ├────models.py │ ├────serializers.py │ ├────urls.py │ ├────urls_api.py │ ├────utils.py │ ├────views.py │ └────views_api.py ├────README.md ├────requirements.txt ├────tb3/ │ ├────__init__.py │ ├────asgi.py │ ├────jinja_env.py │ ├────middlewares.py │ ├────schema.py │ ├────settings.py │ ├────sms_aliyun.py │ ├────urls.py │ ├────utils.py │ └────wsgi.py ├────tb3_uwsgi.ini ├────templates/ │ ├────oa/ │ │ ├────keg_problems.html │ │ ├────product_batch_cost.html │ │ ├────product_batch_cost_list.html │ │ ├────product_batch_material_list.html │ │ ├────product_sale_static.html │ │ ├────products.html │ │ ├────sales_static.html │ │ ├────staff_sales_analysis.html │ │ ├────storage_static.html │ │ └────tank_state_list.html │ ├────pagination.html │ └────wiki/ │ │ ├────base.html │ │ └────view.html ├────uwsgi/ │ └────__init__.py └────uwsgi_params ``` ### 1.2. 项目依赖 - Nginx - python 3.6-3.8 ## 2. 服务安装与升级 ### 2.1. 服务安装 #### 2.1.1. 拉取代码 进入 /opt 目录拉取代码 ```shell git clone https://github.com/ginguocun/tb3.git ``` #### 2.1.2. 进入指定的路径 ```sh cd /opt/tb3 ``` #### 2.1.3. 启动虚拟环境 ```sh python3 -m venv venv source venv/bin/activate ``` #### 2.1.4. 更新 setuptools pip ```sh pip3 install --upgrade setuptools pip ``` #### 2.1.5. 安装依赖 ``` pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ``` #### 2.1.6. 配置 settings 文件 在 tb3/tb3 目录下面添加配置文件 settings.py,并且根据里面的 todo 更新配置 ```python import os from datetime import timedelta from django.urls import reverse_lazy BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # todo 1. 更改 SECRET_KEY SECRET_KEY = 'hrvrbg%+4i2di^m%8a7wrf6mjk7*e0cbv3e%d39m$8@cop(1' # todo 2. 生产环境的 DEBUG 改为 False DEBUG = True LOGIN_URL = '/wiki/_accounts/login/' # todo 3. 添加网址 ALLOWED_HOSTS = ['********', '127.0.0.1', 'localhost'] INSTALLED_APPS = [ 'simpleui', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_filters', 'oa', # wiki 'django.contrib.sites.apps.SitesConfig', 'django.contrib.humanize.apps.HumanizeConfig', 'django_nyt.apps.DjangoNytConfig', 'mptt', 'sekizai', 'sorl.thumbnail', 'wiki.apps.WikiConfig', 'wiki.plugins.attachments.apps.AttachmentsConfig', 'wiki.plugins.notifications.apps.NotificationsConfig', 'wiki.plugins.images.apps.ImagesConfig', 'wiki.plugins.macros.apps.MacrosConfig', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'tb3.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', # wiki 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', "sekizai.context_processors.sekizai", ], }, } ] # wiki WIKI_ACCOUNT_HANDLING = True WIKI_ACCOUNT_SIGNUP_ALLOWED = False LOGIN_REDIRECT_URL = reverse_lazy('wiki:get', kwargs={'path': ''}) SITE_ID = 1 WIKI_ANONYMOUS = True WIKI_CAN_ADMIN = True WIKI_CAN_ASSIGN = True WIKI_CAN_ASSIGN_OWNER = True WIKI_CAN_CHANGE_PERMISSIONS = True WSGI_APPLICATION = 'tb3.wsgi.application' # todo 4. 配置数据库 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, # 'default': { # 'ENGINE': 'django.db.backends.postgresql_psycopg2', # 'NAME': 'tr', # 'USER': '********', # 'PASSWORD': '********', # 'HOST': 'localhost', # 'PORT': '5432', # }, } # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True # USE_TZ = True DEFAULT_CHARSET = 'utf-8' FILE_CHARSET = 'utf-8' MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static/') # STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), os.path.join(BASE_DIR, "front/dist/static")] STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', ) SIMPLEUI_INDEX = '/ht/' SIMPLEUI_STATIC_OFFLINE = True SIMPLEUI_HOME_INFO = False SIMPLEUI_ANALYSIS = False SIMPLEUI_DEFAULT_ICON = False SIMPLEUI_CONFIG = { 'system_keep': True, 'dynamic': True, 'menu_display': ['原料管理', '原料入库', '产品管理', '生产管理', '用户权限', '库存统计', '销售统计', '管理模块'], 'menus': [ { 'name': '原料管理', 'icon': 'fa fa-file', 'models': [ { 'name': '原料名称', 'icon': 'fa fa-tint', 'url': '/ht/oa/material/' }, { 'name': '原料批次', 'icon': 'fa fa-question-circle', 'url': '/ht/oa/materialbatch/' }, { 'name': '原料出库', 'icon': 'fa fa-arrow-circle-right', 'url': '/ht/oa/materialoutorder/' }, { 'name': '原料供应商', 'icon': 'fa fa-users', 'url': '/ht/oa/supplier/' }, ] }, { 'name': '原料入库', 'icon': 'fa fa-arrow-circle-left', 'models': [ { 'name': '采购申请', 'icon': 'fa fa-plus-square', 'url': '/ht/oa/materialpurchaseorder/' }, { 'name': '原料入库', 'icon': 'fa fa-arrow-circle-left', 'url': '/ht/oa/materialinorder/' }, { 'name': '入库质检', 'icon': 'fa fa-check-circle', 'url': '/ht/oa/materialin/' }, ] }, { 'name': '产品管理', 'icon': 'fa fa-beer', 'models': [ { 'name': '产品名称', 'url': '/ht/oa/productname/', 'icon': 'fa fa-beer' }, { 'name': '产品包装', 'icon': 'fa fa-shopping-bag', 'url': '/ht/oa/productpackage/' }, { 'name': '包装组合', 'icon': 'fa fa-briefcase', 'url': '/ht/oa/packagegroup/' }, ] }, { 'name': '生产管理', 'icon': 'fa fa-hourglass', 'models': [ { 'name': '生产批次', 'icon': 'fa fa-hourglass-start', 'url': '/ht/oa/productbatch/' }, { 'name': '糖化批次', 'icon': 'fa fa-hourglass-half', 'url': '/ht/oa/brewbatch/' }, { 'name': '发酵监控', 'icon': 'fa fa-pen', 'url': '/ht/oa/fermentmonitor/' }, { 'name': '操作记录', 'icon': 'fa fa-cogs', 'url': '/ht/oa/processrecord/' }, { 'name': '灌装批次', 'icon': 'fa fa-hourglass-end', 'url': '/ht/oa/packbatch/' }, { 'name': '组合批次', 'icon': 'fa fa-archive', 'url': '/ht/oa/groupbatch/' }, ] }, { 'name': '用户权限', 'icon': 'fas fa-user-shield', 'models': [ { 'name': '用户列表', 'icon': 'fa fa-user', 'url': '/ht/oa/wxuser/' }, { 'name': '权限分组', 'icon': 'fa fa-users-cog', 'url': '/ht/auth/group/' }, # { # 'name': '用户等级', # 'icon': 'fa fa-id-badge', # 'url': '/ht/oa/userlevel/' # }, { 'name': '员工列表', 'icon': 'fa fa-address-card', 'url': '/ht/oa/staff/' }, ] }, { 'name': '库存统计', 'icon': 'fa fa-bookmark', 'models': [ { 'name': '统计结果', 'url': '/page/storages/', 'icon': 'fa fa-paper-plane' }, { 'name': '数据导入', 'icon': 'fa fa-upload', 'url': '/ht/oa/storagecalculate/' }, { 'name': '产品对应', 'url': '/ht/oa/storageproductinfo/', 'icon': 'fa fa-link' }, { 'name': '数据明细', 'url': '/ht/oa/storage/', 'icon': 'fa fa-sitemap' }, ] }, { 'name': '销售统计', 'icon': 'fa fa-address-book', 'models': [ { 'name': '统计结果', 'icon': 'fa fa-print', 'url': '/page/product_sales/' }, { 'name': '客户列表', 'icon': 'fa fa-user-circle', 'url': '/ht/oa/customer/' }, { 'name': '客户反馈', 'url': '/ht/oa/feedback/', 'icon': 'fa fa-comments' }, { 'name': '订单列表', 'url': '/ht/oa/salesrecord/', 'icon': 'fa fa-shopping-cart' }, { 'name': '订单导入', 'url': '/ht/oa/salesrecordupload/', 'icon': 'fa fa-cart-plus' }, ] }, { 'name': '管理模块', 'icon': 'fa fa-book', 'models': [ { 'name': '包装单位', 'url': '/ht/oa/packageunit/', 'icon': 'fa fa-tag' }, { 'name': '仓库管理', 'url': '/ht/oa/warehouse/', 'icon': 'fa fa-shopping-basket' }, { 'name': '设备列表', 'url': '/ht/oa/equipment/', 'icon': 'fa fa-puzzle-piece' }, { 'name': '文件列表', 'url': '/ht/oa/document/', 'icon': 'fa fa-file' }, ] }, ] } AUTH_USER_MODEL = 'oa.WxUser' CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework_simplejwt.authentication.JWTAuthentication', # 通过 JWT 进行用户验证,验证过程需要访问数据库 # 'rest_framework_simplejwt.authentication.JWTTokenUserAuthentication', # 通过 JWT 的 Token 进行用户验证,验证过程不需要访问数据库 ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ), 'DEFAULT_PAGINATION_CLASS': 'tb3.utils.NormalResultsSetPagination', 'PAGE_SIZE': 10, 'DEFAULT_FILTER_BACKENDS': ( 'rest_framework.filters.SearchFilter', 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter' ), 'DEFAULT_SCHEMA_CLASS': 'tb3.schema.ManualSchema' # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' } # todo 5. 配置 JWT_SIGNING_KEY JWT_SIGNING_KEY = 'HdYEA+b4sBdFI7bBraWGcNN2ZbXZCxGBi+ufiuSU625hHdT' SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(days=15), 'REFRESH_TOKEN_LIFETIME': timedelta(days=15), 'ROTATE_REFRESH_TOKENS': False, 'BLACKLIST_AFTER_ROTATION': True, 'ALGORITHM': 'HS256', 'SIGNING_KEY': JWT_SIGNING_KEY, 'VERIFYING_KEY': None, 'AUTH_HEADER_TYPES': ('Bearer',), 'USER_ID_FIELD': 'id', 'USER_ID_CLAIM': 'user_id', 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 'TOKEN_TYPE_CLAIM': 'token_type', 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', 'SLIDING_TOKEN_LIFETIME': timedelta(days=15), 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=15), } BASE_LOG_DIR = os.path.join(BASE_DIR, "log") LOGGING = { 'version': 1, # version表示版本,一般不用改 # disable_existing_loggers表示弃用已经存在的日志,True表示弃用,False表示不弃用。 'disable_existing_loggers': True, # 禁用已经存在的logger实例 # 日志文件的格式 'formatters': { # 详细的日志格式 'verbose': { 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' '[%(levelname)s][%(message)s]' }, # 标准的日志格式 'standard': { 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' }, # 简单的日志格式 'simple': { 'format': '[%(levelname)s][ %(message)s]' }, # 定义一个特殊的日志格式 'collect': { 'format': '%(message)s' } }, # 上面的日志格式可以自己随便定义几个 # 过滤器 'filters': { }, # 处理器 'handlers': { # 在终端打印 'console': { 'level': 'INFO', # 日志的级别 'class': 'logging.StreamHandler', # 'formatter': 'standard' # 使用哪种日志格式 }, # 默认的 'default': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "tb3_info.log"), # 日志文件的位置 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 3, # 最多备份几个 'formatter': 'standard', # 使用哪种日志格式 'encoding': 'utf-8', # 保存的格式 }, # 专门用来记错误日志 'error': { 'level': 'ERROR', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "tb3_err.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 5, 'formatter': 'standard', 'encoding': 'utf-8', }, # 专门定义一个收集特定信息的日志 'collect': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切 'filename': os.path.join(BASE_LOG_DIR, "tb3_debug.log"), 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 5, 'formatter': 'standard', 'encoding': "utf-8" } }, # 上面的处理器可以根据自己的需求来配置 'loggers': { # 默认的logger应用如下配置 '': { 'handlers': ['default', 'console', 'error', 'collect'], # 上线之后可以把'console'移除 'level': 'DEBUG', 'propagate': True, # 向不向更高级别的logger传递 }, # 'collect': { # 'handlers': ['console', 'collect'], # 'level': 'INFO', # }, 'django.request': { 'handlers': ['console', 'default'], 'propagate': True, 'level': 'DEBUG', }, 'django.db.backends': { 'handlers': ['collect'], 'propagate': True, 'level': 'DEBUG', }, }, } ``` #### 2.1.7. 迁移数据库 ```sh python3 manage.py migrate ``` #### 2.1.8. 创建超级用户 ```sh python3 manage.py createsuperuser ``` 运行以上命令,输入用户名和密码 #### 2.1.9. 项目启动 测试服务启动 ```sh python3 manage.py runserver 0.0.0.0:80 ``` 在浏览器的地址输入框输入ip,然后后面添加 /ht/ ,看能否跳转到登录页面,如果跳转成功说明服务已经正常启动了。但是用户直接访问 Django 服务的部署方式不适合生产环境。 ### 2.2. 服务部署 服务采用Nginx+uwsgi+Django 的方式进行部署。 ``` the web client <-> Nginx <-> the socket <-> uwsgi <-> Django ``` 可以参照https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html #### 2.2.1. 安装和配置 Nginx 下载和安装 Nginx ```sh apt-get install -y nano nginx ``` 拷贝配置文件 ```sh cp /opt/tb3/nginx.conf /etc/nginx/sites-available/default ``` 用 nano 编辑器打开Nginx 的配置文件 default ```sh nano /etc/nginx/sites-available/default ``` 将文件里面的 server_name 改为自己的网址,然后运行以下代码测试配置是否正常。 ```sh nginx -t ``` 如果输出如下,说明配置已经成功。否则可能有问题。 ``` nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` 启动 nginx 服务,可以运行以下命令。 ```sh /etc/init.d/nginx start ``` 如果启动失败,可能是由于 80 端口被占用,需要先关闭相关的服务。 如果服务已经启动,配置文件更新了,可以运行以下命令重新加载配置,使配置生效。 ```sh nginx -s reload ``` 服务正常启动以后访问 ip 可以看到 **Welcome to nginx!** 字样。 ### 2.2.2. 启动 uwsgi 可以使用 tb3_uwsgi.ini 进行项目启动,运行以下代码 ``` cd /opt/tb3 && source venv/bin/activate uwsgi --ini /opt/tb3/tb3_uwsgi.ini ``` ### 2.3. 服务升级 服务升级需要的命令 ```sh cd /opt/tb3 && source venv/bin/activate git pull python3 manage.py migrate ps auxw | grep tb3_uwsgi uwsgi --reload /opt/tb3/uwsgi/uwsgi.pid ps auxw | grep tb3_uwsgi ``` #### 2.3.1. 进入指定的路径 ```sh cd /opt/tb3 ``` #### 2.3.2. 启动虚拟环境 ```sh source venv/bin/activate ``` #### 2.3.3. 拉取最新的代码 ```sh git pull pip install -r requirements.txt python3 manage.py migrate ``` #### 2.3.4. 重启服务 ```sh ps auxw | grep tb3_uwsgi uwsgi --reload /opt/tb3/uwsgi/uwsgi.pid ``` 服务启动命令 ```sh uwsgi --ini /opt/tb3/tb3_uwsgi.ini ``` #### 2.3.5. 数据迁移 ```bash python3 manage.py dumpdata --all --format=json > mysite_all_data.json python3 manage.py loaddata mysite_all_data.json ``` ## 3. 模块介绍 ### 3.1. 原料管理模块 #### 3.1.1. 原料关联关系 ```mermaid graph LR Supplier[供应商]-->MaterialPurchaseOrder MaterialPurchaseOrder[采购订单]-->MaterialInOrder MaterialPurchaseOrder-->MaterialPurchaseOrderItem MaterialCategory[原料归类]-->Material Material[原料]-->MaterialPurchaseOrderItem Material-->MaterialBatch MaterialBatch[原料批次]-->MaterialIn MaterialBatch-->MaterialBatchStore(原料库存) MaterialBatch-->MaterialOut PackageUnit[包装规格]-->MaterialPurchaseOrderItem PackageUnit-->MaterialBatch quarify(质检)-.-MaterialBatch MaterialInOrder[入库单]-->MaterialIn[入库明细] MaterialPurchaseOrderItem[采购明细]-->MaterialIn Warehouse[仓库]-->MaterialIn Warehouse-->MaterialBatchStore(原料批次库存) Warehouse-->MaterialOut BrewBatch[糖化批次]-->MaterialOutOrder ProductBatch-->BrewBatch PackBatch[灌装批次]-->MaterialOutOrder ProductBatch[酿造批次]-->PackBatch ProductBatch-->ProcessRecord[操作记录] ProcessRecord-->MaterialOutOrder ProductBatch[酿造批次]-->GroupBatch[组合批次] GroupBatch-->MaterialOutOrder check[盘点]-->MaterialOutOrder MaterialOutOrder[出库单]-->MaterialOut[出库明细] MaterialIn-->MaterialBatchStore MaterialOut-->MaterialBatchStore ``` #### 3.1.2. 原料入库联动更新 ```mermaid graph TB MaterialPurchaseOrder --> MaterialPurchaseOrderItem MaterialPurchaseOrderItem --> MaterialPurchaseOrder MaterialIn --> MaterialBatch MaterialIn --> MaterialBatchStorage MaterialIn --> MaterialPurchaseOrderItem ``` ### 3.2. 产品的成本管理 系统里面的产品成本分为**生产成本**和**出厂成本**。**生产成本**意味着这款产品的实际生产所需的成本,**出厂成本**为销售公司的结算价格,通常来说**出厂成本**应该高于**生产成本**。 ``` 出厂成本 > 生产成本 ``` #### 3.2.1. 部门销售统计 销售统计是基于导入的销售明细(SalesRecord,系统对应为【销售统计】>【订单列表】),来自于上海的智慧商贸系统(七色米),通过一张中间表(【产品对应】)关联到包装组合(PackageGroup)。部门销售统计模块的成本是基于**出厂成本**,也可以理解为部门结算价格。 ```mermaid erDiagram SalesRecord ||--|{ StorageProductInfo : "contains" StorageProductInfo ||--|| PackageGroup : "related" ``` 检查成本统计的步骤: 1. 在【销售统计】> 【订单列表】页面,【有出厂成本】- 选择【否】,查看没有出厂成本的明细; 1. 在【产品管理】> 【包装组合】页面,【有出厂成本】- 选择【否】,查看没有出厂成本的明细; 1. 在【产品管理】> 【产品价格】页面,【渠道】选择【3-销售部门】,查看是否已经录入了产品成本;