提交 727366d3 authored 作者: 贺阳's avatar 贺阳

增加托盘管理模块

上级 a8815333
from . import wizard
from . import models
from . import controllers
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': '清关托盘管理模块',
'version': '1.2',
'summary': '托盘管理模块',
'sequence': 10,
'description': """
托盘管理模块
1) 定义托盘对象,并完成托盘的导入功能
2) 定义托盘生成报关单的功能
3) 定义清关节点,并在托盘上更新清关节点.
""",
'category': 'Pallet Management',
'website': 'https://www.yizuo.ltd',
'depends': ['base', 'mail', 'web', 'ccs_base'],
'data': [
'security/pallet_security.xml',
'security/ir.model.access.csv',
'views/cc_pallet_view.xml',
'views/menu_view.xml',
'wizard/pallet_batch_wizard_view.xml',
'wizard/pallet_print_wizard_view.xml',
'reports/pallet_label_report.xml',
],
'installable': True,
'application': True,
'assets': {
},
'license': 'AGPL-3',
}
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
</data>
</odoo>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- <record id="sequence_cc_pallet" model="ir.sequence">
<field name="name">托盘编码规则</field>
<field name="code">cc.pallet</field>
<field name="padding">2</field>
<field name="number_next_actual">1</field>
<field name="company_id" eval="False"/>
</record> -->
</data>
</odoo>
\ No newline at end of file
<odoo>
<data>
</data>
</odoo>
\ No newline at end of file
from . import cc_pallet
# -*- coding: utf-8 -*-
# 导入日志
import json
import logging
from datetime import timedelta
import pytz
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
# 获取日志
_logger = logging.getLogger(__name__)
class CcPallet(models.Model):
# 模型名称
_name = 'cc.pallet'
_inherit = ['mail.thread', 'mail.activity.mixin']
# 模型描述
_description = 'Pallet'
# 托盘号(年月+4位数,不能重复生成)
name = fields.Char(
string='托盘号',
required=True,
readonly=True,
copy=False,
default=lambda self: _('New'),
help='托盘号格式:年月+4位数'
)
# 托盘归属快递
express_company_id = fields.Many2one(
'res.partner',
string='托盘归属快递',
required=True,
domain=[('is_express_company', '=', True)],
help='选择尾程快递公司'
)
# 创建时间
create_date = fields.Datetime(
string='创建时间',
readonly=True,
default=fields.Datetime.now
)
# 创建人
create_uid = fields.Many2one(
'res.users',
string='创建人',
readonly=True,
default=lambda self: self.env.user
)
# 使用状态
usage_state = fields.Selection([
('unused', '未使用'),
('used', '已使用')
], string='使用状态', default='unused', required=True, tracking=True)
# 打印状态
print_state = fields.Selection([
('unprinted', '未打印'),
('printed', '已打印')
], string='打印状态', default='unprinted', required=True, tracking=True)
# 托盘理货时间
sorting_time = fields.Datetime(
string='托盘理货时间',
help='托盘理货完成的时间'
)
# 托盘交货时间
delivery_time = fields.Datetime(
string='托盘交货时间',
help='托盘交付给客户的时间'
)
# 托盘装载大包
package_ids = fields.One2many(
'stock.picking',
'pallet_id',
string='托盘装载大包',
help='装载在此托盘上的大包'
)
# 大包数量
package_count = fields.Integer(
string='托盘装载大包数',
compute='_compute_package_count',
store=True
)
# 使用客户
customer_id = fields.Many2one(
'res.partner',
string='使用客户',
domain=[('is_company', '=', True)],
help='使用此托盘的客户'
)
# 托盘交付车牌号
delivery_plate_number = fields.Char(
string='托盘交付车牌号',
help='交付托盘时的车牌号'
)
# 计算字段:大包数量
@api.depends('package_ids')
def _compute_package_count(self):
for record in self:
record.package_count = len(record.package_ids)
# 重写create方法,自动生成托盘号
@api.model
def create(self, vals):
if vals.get('name', _('New')) == _('New'):
# 生成托盘号:年月+4位数
now = fields.Datetime.now()
year_month = now.strftime('%Y%m')
# 查找当月最大的编号
last_pallet = self.search([
('name', 'like', year_month + '%')
], order='name desc', limit=1)
if last_pallet and last_pallet.name:
# 提取最后4位数字
last_number = int(last_pallet.name[-4:])
new_number = last_number + 1
else:
new_number = 1
# 生成新的托盘号
vals['name'] = f"{year_month}{new_number:04d}"
return super(CcPallet, self).create(vals)
# 当大包关联到托盘时,自动更新使用状态
@api.model
def update_usage_state(self):
"""更新托盘使用状态"""
for pallet in self:
if pallet.package_ids:
pallet.usage_state = 'used'
else:
pallet.usage_state = 'unused'
def action_print_labels(self):
"""打印标签动作"""
return {
'type': 'ir.actions.act_window',
'name': _('打印托盘标签'),
'res_model': 'pallet.print.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_pallet_ids': [(6, 0, self.ids)],
},
}
def action_view_packages(self):
"""查看大包动作"""
return {
'type': 'ir.actions.act_window',
'name': _('托盘大包'),
'res_model': 'stock.picking',
'view_mode': 'list,form',
'domain': [('pallet_id', '=', self.id)],
'context': {'default_pallet_id': self.id},
}
def action_batch_create(self):
"""批量创建托盘动作"""
return {
'type': 'ir.actions.act_window',
'name': _('批量创建托盘'),
'res_model': 'pallet.batch.wizard',
'view_mode': 'form',
'target': 'new',
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 托盘标签打印报告 -->
<record id="action_pallet_label_report" model="ir.actions.report">
<field name="name">托盘标签</field>
<field name="model">cc.pallet</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">ccs_pallet.pallet_label_report</field>
<field name="report_file">ccs_pallet.pallet_label_report</field>
<field name="binding_model_id" ref="model_cc_pallet"/>
<field name="binding_type">report</field>
</record>
<!-- 托盘标签模板 -->
<template id="pallet_label_report">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="pallet">
<div class="page" style="width: 10cm; height: 15cm; margin: 0; padding: 0.5cm; border: 1px solid #ccc; page-break-after: always;">
<!-- 客户名称和快递简称 -->
<div style="text-align: center; margin-bottom: 1cm;">
<h2 style="margin: 0; font-size: 24px; font-weight: bold;">
<t t-esc="pallet.customer_id.name or 'TIKTOK'"/> / <t t-esc="pallet.express_company_id.name or 'RM'"/>
</h2>
</div>
<!-- 条形码区域 -->
<div style="text-align: center; margin: 2cm 0; padding: 1cm; border: 2px dashed #999; background-color: #f9f9f9;">
<div style="font-size: 18px; color: #666; margin-bottom: 0.5cm;">
条形二维码
</div>
<!-- 这里可以集成条形码生成库 -->
<div style="font-size: 14px; color: #999;">
<t t-esc="pallet.name"/>
</div>
</div>
<!-- 托盘号 -->
<div style="text-align: center; margin-top: 1cm;">
<h1 style="margin: 0; font-size: 28px; font-weight: bold;">
<t t-esc="pallet.name"/>
</h1>
</div>
</div>
</t>
</t>
</template>
</odoo>
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_cc_pallet_user,cc.pallet.user,model_cc_pallet,base.group_user,1,1,1,0
access_cc_pallet_manager,cc.pallet.manager,model_cc_pallet,base.group_system,1,1,1,1
access_pallet_batch_wizard_user,pallet.batch.wizard.user,model_pallet_batch_wizard,base.group_user,1,1,1,0
access_pallet_print_wizard_user,pallet.print.wizard.user,model_pallet_print_wizard,base.group_user,1,1,1,0
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record model="ir.module.category" id="ccs_base.module_category_pallet">
<field name="name">Pallet Information</field>
<field name="description">Pallet Information</field>
<field name="sequence">7</field>
</record>
<record id="group_pallet_manager" model="res.groups">
<field name="name">Pallet Manager</field>
<field name="category_id" ref="ccs_base.module_category_pallet"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
</data>
</odoo>
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- 托盘管理列表视图 -->
<record id="view_cc_pallet_list" model="ir.ui.view">
<field name="name">cc.pallet.list</field>
<field name="model">cc.pallet</field>
<field name="arch" type="xml">
<list string="托盘管理" multi_edit="1">
<field name="name" string="托盘号"/>
<field name="express_company_id" string="托盘归属快递"/>
<field name="usage_state" string="使用状态" decoration-success="usage_state == 'unused'" decoration-warning="usage_state == 'used'"/>
<field name="print_state" string="打印状态" decoration-info="print_state == 'unprinted'" decoration-success="print_state == 'printed'"/>
<field name="sorting_time" string="托盘理货时间"/>
<field name="delivery_time" string="托盘交货时间"/>
<field name="package_count" string="托盘装载大包数"/>
<field name="delivery_plate_number" string="托盘交付车牌号"/>
</list>
</field>
</record>
<!-- 托盘管理表单视图 -->
<record id="view_cc_pallet_form" model="ir.ui.view">
<field name="name">cc.pallet.form</field>
<field name="model">cc.pallet</field>
<field name="arch" type="xml">
<form string="托盘管理">
<header>
<button name="action_print_labels" string="打印标签" type="object" class="btn-primary" icon="fa-print"/>
<field name="usage_state" widget="statusbar" statusbar_visible="unused,used"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_packages" type="object" class="oe_stat_button" icon="fa-cubes">
<field name="package_count" widget="statinfo" string="大包"/>
</button>
</div>
<div class="oe_title">
<h1>
<field name="name" readonly="1"/>
</h1>
</div>
<group>
<group string="基本信息">
<field name="express_company_id" required="1"/>
<field name="customer_id"/>
<field name="delivery_plate_number"/>
</group>
<group string="状态信息">
<field name="print_state"/>
<field name="sorting_time"/>
<field name="delivery_time"/>
</group>
</group>
<notebook>
<page string="大包信息" name="packages">
<field name="package_ids">
<list>
<field name="name"/>
<field name="partner_id"/>
<field name="state"/>
</list>
</field>
</page>
</notebook>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- 托盘管理搜索视图 -->
<record id="view_cc_pallet_search" model="ir.ui.view">
<field name="name">cc.pallet.search</field>
<field name="model">cc.pallet</field>
<field name="arch" type="xml">
<search string="托盘搜索">
<field name="name" string="托盘号" filter_domain="[('name', 'ilike', self)]"/>
<field name="express_company_id" string="快递公司"/>
<field name="customer_id" string="使用客户"/>
<field name="delivery_plate_number" string="车牌号"/>
<separator/>
<filter name="filter_unused" string="未使用" domain="[('usage_state', '=', 'unused')]"/>
<filter name="filter_used" string="已使用" domain="[('usage_state', '=', 'used')]"/>
<filter name="filter_unprinted" string="未打印" domain="[('print_state', '=', 'unprinted')]"/>
<filter name="filter_printed" string="已打印" domain="[('print_state', '=', 'printed')]"/>
<separator/>
<group expand="0" string="分组">
<filter name="group_by_express" string="尾程快递" context="{'group_by': 'express_company_id'}"/>
<filter name="group_by_print_state" string="打印状态" context="{'group_by': 'print_state'}"/>
<filter name="group_by_usage_state" string="使用状态" context="{'group_by': 'usage_state'}"/>
</group>
</search>
</field>
</record>
<!-- 托盘管理动作 -->
<record id="action_cc_pallet" model="ir.actions.act_window">
<field name="name">托盘管理</field>
<field name="res_model">cc.pallet</field>
<field name="view_mode">list,form</field>
<field name="context">{}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
创建托盘
</p>
<p>
托盘管理系统用于管理仓库中的托盘,包括托盘号生成、状态跟踪和标签打印。
</p>
</field>
</record>
<!-- 批量创建托盘动作 -->
<record id="action_cc_pallet_batch_create" model="ir.actions.act_window">
<field name="name">批量创建托盘</field>
<field name="res_model">pallet.batch.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{}</field>
</record>
<!-- 菜单项 -->
<menuitem id="menu_cc_pallet_root"
name="托盘管理"
sequence="10"/>
<menuitem id="menu_cc_pallet"
name="托盘管理"
parent="menu_cc_pallet_root"
action="action_cc_pallet"
sequence="10"/>
<menuitem id="menu_cc_pallet_batch_create"
name="批量创建托盘"
parent="menu_cc_pallet_root"
action="action_cc_pallet_batch_create"
sequence="20"/>
</odoo>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
</data>
</odoo>
\ No newline at end of file
# -*- coding: utf-8 -*-
from . import pallet_batch_wizard
from . import pallet_print_wizard
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
class PalletBatchWizard(models.TransientModel):
_name = 'pallet.batch.wizard'
_description = '批量创建托盘向导'
express_company_id = fields.Many2one(
'res.partner',
string='托盘归属快递',
required=True,
domain=[('is_express_company', '=', True)],
help='选择尾程快递公司'
)
quantity = fields.Integer(
string='创建数量',
required=True,
default=1,
help='要创建的托盘数量'
)
start_number = fields.Char(
string='托盘创建开始编号',
readonly=True,
help='本次创建托盘的起始编号'
)
customer_id = fields.Many2one(
'res.partner',
string='使用客户',
domain=[('is_company', '=', True)],
help='使用此托盘的客户(可选)'
)
@api.onchange('express_company_id', 'quantity')
def _onchange_express_company(self):
"""当选择快递公司或数量时,计算开始编号"""
if self.express_company_id and self.quantity:
# 获取当前年月
now = fields.Datetime.now()
year_month = now.strftime('%Y%m')
# 查找当月最大的编号
last_pallet = self.env['cc.pallet'].search([
('name', 'like', year_month + '%')
], order='name desc', limit=1)
if last_pallet and last_pallet.name:
# 提取最后4位数字
last_number = int(last_pallet.name[-4:])
start_number = last_number + 1
else:
start_number = 1
self.start_number = f"{year_month}{start_number:04d}"
@api.constrains('quantity')
def _check_quantity(self):
"""验证创建数量"""
if self.quantity <= 0:
raise ValidationError(_('创建数量必须大于0'))
if self.quantity > 1000:
raise ValidationError(_('单次创建数量不能超过1000个'))
def action_confirm(self):
"""确认创建托盘"""
self.ensure_one()
# 创建托盘记录
pallets = []
now = fields.Datetime.now()
year_month = now.strftime('%Y%m')
# 获取起始编号
if self.start_number:
start_num = int(self.start_number[-4:])
else:
start_num = 1
for i in range(self.quantity):
pallet_name = f"{year_month}{start_num + i:04d}"
# 检查托盘号是否已存在
existing_pallet = self.env['cc.pallet'].search([
('name', '=', pallet_name)
])
if existing_pallet:
raise UserError(_('托盘号 %s 已存在,请检查系统数据') % pallet_name)
pallet_vals = {
'name': pallet_name,
'express_company_id': self.express_company_id.id,
'customer_id': self.customer_id.id if self.customer_id else False,
}
pallet = self.env['cc.pallet'].create(pallet_vals)
pallets.append(pallet)
# 返回创建结果
return {
'type': 'ir.actions.act_window',
'name': _('批量创建托盘结果'),
'res_model': 'cc.pallet',
'view_mode': 'list,form',
'domain': [('id', 'in', [p.id for p in pallets])],
'context': {'create': False},
}
def action_confirm_and_print(self):
"""确认创建托盘并打印"""
self.ensure_one()
# 先创建托盘
result = self.action_confirm()
# 获取创建的托盘
pallets = self.env['cc.pallet'].search([
('id', 'in', result['domain'][0][2])
])
# 生成打印文件
return self._generate_print_report(pallets)
def _generate_print_report(self, pallets):
"""生成打印报告"""
# 这里需要实现PDF生成逻辑
# 暂时返回一个简单的动作
return {
'type': 'ir.actions.report',
'report_name': 'ccs_pallet.pallet_label_report',
'report_type': 'qweb-pdf',
'data': {
'ids': pallets.ids,
'model': 'cc.pallet',
},
'context': self.env.context,
}
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- 批量创建托盘向导表单视图 -->
<record id="view_pallet_batch_wizard_form" model="ir.ui.view">
<field name="name">pallet.batch.wizard.form</field>
<field name="model">pallet.batch.wizard</field>
<field name="arch" type="xml">
<form string="批量创建托盘">
<sheet>
<div class="oe_title">
<h1>批量创建托盘</h1>
</div>
<group>
<group string="创建信息">
<field name="express_company_id" required="1"/>
<field name="quantity" required="1"/>
<field name="customer_id"/>
</group>
<group string="编号信息">
<field name="start_number" readonly="1"/>
</group>
</group>
<div class="alert alert-info" role="alert">
<strong>提示:</strong>
<ul>
<li>托盘号将按升序自动生成</li>
<li>格式:年月+4位数(如:2025010001)</li>
<li>单次最多可创建1000个托盘</li>
</ul>
</div>
</sheet>
<footer>
<button name="action_confirm" string="确认" type="object" class="btn-primary"/>
<button name="action_confirm_and_print" string="确认并打印" type="object" class="btn-success"/>
<button string="取消" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
<!-- 批量创建托盘向导动作 -->
<record id="action_pallet_batch_wizard" model="ir.actions.act_window">
<field name="name">批量创建托盘</field>
<field name="res_model">pallet.batch.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{}</field>
</record>
</odoo>
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import UserError
class PalletPrintWizard(models.TransientModel):
_name = 'pallet.print.wizard'
_description = '托盘打印向导'
pallet_ids = fields.Many2many(
'cc.pallet',
string='托盘',
required=True,
help='选择要打印的托盘'
)
pallet_count = fields.Integer(
string='托盘数量',
compute='_compute_pallet_count',
store=True
)
@api.depends('pallet_ids')
def _compute_pallet_count(self):
for record in self:
record.pallet_count = len(record.pallet_ids)
def action_print(self):
"""执行打印"""
self.ensure_one()
if not self.pallet_ids:
raise UserError(_('请选择要打印的托盘'))
# 更新打印状态为已打印
self.pallet_ids.write({'print_state': 'printed'})
# 生成打印报告
return self._generate_print_report()
def _generate_print_report(self):
"""生成打印报告"""
return {
'type': 'ir.actions.report',
'report_name': 'ccs_pallet.pallet_label_report',
'report_type': 'qweb-pdf',
'data': {
'ids': self.pallet_ids.ids,
'model': 'cc.pallet',
},
'context': self.env.context,
}
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- 托盘打印向导表单视图 -->
<record id="view_pallet_print_wizard_form" model="ir.ui.view">
<field name="name">pallet.print.wizard.form</field>
<field name="model">pallet.print.wizard</field>
<field name="arch" type="xml">
<form string="托盘打印">
<sheet>
<div class="oe_title">
<h1>托盘标签打印</h1>
</div>
<group>
<field name="pallet_count" readonly="1"/>
</group>
<field name="pallet_ids" widget="many2many_tags" readonly="1"/>
<div class="alert alert-info" role="alert">
<strong>打印说明:</strong>
<ul>
<li>标签尺寸:10cm × 15cm</li>
<li>标签内容:客户名称、尾程快递简称、托盘号条形码</li>
<li>打印后托盘状态将自动更新为"已打印"</li>
</ul>
</div>
</sheet>
<footer>
<button name="action_print" string="确定打印" type="object" class="btn-primary"/>
<button string="取消" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
<!-- 托盘打印向导动作 -->
<record id="action_pallet_print_wizard" model="ir.actions.act_window">
<field name="name">托盘打印</field>
<field name="res_model">pallet.print.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{}</field>
</record>
</odoo>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论