changes
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import fp_label_manual_wizard
|
||||
from . import fp_label_generate_wizard
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
"""Service-type confirmation wizard.
|
||||
|
||||
Opens when fp.receiving.action_generate_outbound_label fires (assuming
|
||||
a label hasn't already been generated). Forces the operator to look at
|
||||
carrier + service tier + weight before the API call is made — cheaper
|
||||
shipping decisions, no surprise charges, and a hard gate against the
|
||||
accidental-double-click bug where every Generate click leaked a fresh
|
||||
FedEx shipment + tracking number.
|
||||
|
||||
Picked service rides through to the FedEx API via the
|
||||
`fp_service_type_override` context key (see CLAUDE.md).
|
||||
"""
|
||||
from odoo import _, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class FpLabelGenerateWizard(models.TransientModel):
|
||||
_name = 'fp.label.generate.wizard'
|
||||
_description = 'Fusion Plating — Confirm Label Generation'
|
||||
|
||||
receiving_id = fields.Many2one(
|
||||
'fp.receiving', required=True, readonly=True, ondelete='cascade',
|
||||
)
|
||||
receiving_name = fields.Char(related='receiving_id.name', readonly=True)
|
||||
carrier_id = fields.Many2one(
|
||||
related='receiving_id.x_fc_carrier_id', readonly=True,
|
||||
)
|
||||
carrier_default_service = fields.Char(
|
||||
compute='_compute_carrier_default_service',
|
||||
string='Carrier Default',
|
||||
help='What the carrier would use if no override is set.',
|
||||
)
|
||||
service_type = fields.Selection(
|
||||
selection='_fp_get_service_type_selection',
|
||||
string='Service Type',
|
||||
help='Leave blank to use the carrier default shown above. Pick '
|
||||
'a faster tier (Priority Overnight, 2Day, etc.) when the '
|
||||
'customer is paying for expedited delivery.',
|
||||
)
|
||||
weight = fields.Float(string='Weight', digits=(10, 3), required=True)
|
||||
weight_uom = fields.Selection(
|
||||
related='receiving_id.x_fc_weight_uom', readonly=True,
|
||||
)
|
||||
length = fields.Float(string='Length', digits=(10, 2))
|
||||
width = fields.Float(string='Width', digits=(10, 2))
|
||||
height = fields.Float(string='Height', digits=(10, 2))
|
||||
dim_uom = fields.Selection(
|
||||
related='receiving_id.x_fc_dim_uom', readonly=True,
|
||||
)
|
||||
|
||||
def _fp_get_service_type_selection(self):
|
||||
# Single source of truth — pulls the curated list from
|
||||
# fp.receiving so both the form dropdown and the wizard stay
|
||||
# in sync. See fp.receiving._FP_USABLE_FEDEX_SERVICES.
|
||||
Receiving = self.env.get('fp.receiving')
|
||||
if Receiving is None:
|
||||
return []
|
||||
return Receiving._fp_get_service_type_selection()
|
||||
|
||||
def _compute_carrier_default_service(self):
|
||||
for wiz in self:
|
||||
carrier = wiz.carrier_id
|
||||
if not carrier or 'fedex_rest_service_type' not in carrier._fields:
|
||||
wiz.carrier_default_service = ''
|
||||
continue
|
||||
code = carrier.fedex_rest_service_type or ''
|
||||
label = dict(
|
||||
carrier._fields['fedex_rest_service_type'].selection
|
||||
).get(code, code)
|
||||
wiz.carrier_default_service = label or _('(none set)')
|
||||
|
||||
@classmethod
|
||||
def _fp_default_from_receiving(cls, env, rec):
|
||||
"""Build the wizard create-vals from a receiving record."""
|
||||
return {
|
||||
'receiving_id': rec.id,
|
||||
'service_type': rec.x_fc_outbound_service_type or False,
|
||||
'weight': rec.x_fc_weight or 0.0,
|
||||
'length': rec.x_fc_length or 0.0,
|
||||
'width': rec.x_fc_width or 0.0,
|
||||
'height': rec.x_fc_height or 0.0,
|
||||
}
|
||||
|
||||
def action_confirm(self):
|
||||
"""Apply the operator's choices back to the receiving, then
|
||||
delegate to fp.receiving._fp_actually_generate_outbound_label
|
||||
which makes the API call. Wizard closes; result action is the
|
||||
outbound shipment view (or the manual-fallback wizard on error).
|
||||
"""
|
||||
self.ensure_one()
|
||||
rec = self.receiving_id
|
||||
if not rec:
|
||||
raise UserError(_('Wizard is detached from the receiving.'))
|
||||
if not self.weight or self.weight <= 0:
|
||||
raise UserError(_('Enter a non-zero weight before generating.'))
|
||||
rec.write({
|
||||
'x_fc_outbound_service_type': self.service_type or False,
|
||||
'x_fc_weight': self.weight,
|
||||
'x_fc_length': self.length,
|
||||
'x_fc_width': self.width,
|
||||
'x_fc_height': self.height,
|
||||
})
|
||||
return rec._fp_actually_generate_outbound_label()
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
|
||||
Service-type confirmation wizard. Pops up before every
|
||||
"Generate Outbound Label" so the operator can pick the FedEx
|
||||
service tier (or accept the carrier default) and double-check
|
||||
weight/dimensions before any API call is made.
|
||||
-->
|
||||
<odoo>
|
||||
<record id="view_fp_label_generate_wizard_form" model="ir.ui.view">
|
||||
<field name="name">fp.label.generate.wizard.form</field>
|
||||
<field name="model">fp.label.generate.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Generate Outbound Label">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h2>Generate Label —
|
||||
<field name="receiving_name"
|
||||
readonly="1" nolabel="1" class="oe_inline"/>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="alert alert-info" role="alert">
|
||||
<i class="fa fa-info-circle me-2"/>
|
||||
Confirm the service tier and package details before
|
||||
the carrier API is called. Each generation creates
|
||||
a billable shipment.
|
||||
</div>
|
||||
<group>
|
||||
<group string="Carrier">
|
||||
<field name="carrier_id" readonly="1"/>
|
||||
<field name="carrier_default_service"
|
||||
readonly="1"/>
|
||||
<field name="service_type"
|
||||
placeholder="Use carrier default"/>
|
||||
</group>
|
||||
<group string="Package">
|
||||
<label for="weight"/>
|
||||
<div class="o_row">
|
||||
<field name="weight" class="oe_inline"/>
|
||||
<field name="weight_uom" nolabel="1"
|
||||
readonly="1" class="oe_inline"/>
|
||||
</div>
|
||||
<label for="length" string="Dimensions (L×W×H)"/>
|
||||
<div class="o_row">
|
||||
<field name="length" class="oe_inline"/>
|
||||
<span>×</span>
|
||||
<field name="width" class="oe_inline"/>
|
||||
<span>×</span>
|
||||
<field name="height" class="oe_inline"/>
|
||||
<field name="dim_uom" nolabel="1"
|
||||
readonly="1" class="oe_inline"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="action_confirm" type="object"
|
||||
string="Generate Label" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary"
|
||||
special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user