[IMP] mrp,stock: allow auto-printing + chained (report) actions

This commit adds two linked features:
1- the ability to chain (report) actions
2- the ability to auto-print delivery report at picking validation

Because feature 2 results in the possibility of 2 actions to be returned
at picking validation (due to pre-existing auto-open reception report at
validation option), feature 1 is required to support it. It is expected
that chaining will primarily occur with report actions since chaining of
the other action types does not currently have an applicable use case.
Since this feature is only needed within stock for now, it is
encapsulated within a custom client_action for now that is a lot of copy
paste of action_service.js

This is the first commit to support a series of potential
auto-printable reports.

Part of task: 3046178 - Operation Type
ENT PR: odoo/enterprise#43362

Part-of: odoo/odoo#126791
This commit is contained in:
Tiffany Chang (tic)
2023-10-24 12:38:05 +00:00
parent 5f816d635a
commit 4ebad302a4
3 changed files with 130 additions and 45 deletions
+27 -2
View File
@@ -10,6 +10,7 @@ from markupsafe import escape
from odoo import SUPERUSER_ID, _, api, fields, models
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
from odoo.addons.web.controllers.utils import clean_action
from odoo.exceptions import UserError, ValidationError
from odoo.osv import expression
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, format_datetime, format_date, groupby
@@ -84,6 +85,9 @@ class PickingType(models.Model):
auto_show_reception_report = fields.Boolean(
"Show Reception Report at Validation",
help="If this checkbox is ticked, Odoo will automatically show the reception report (if there are moves to allocate to) when validating.")
auto_print_delivery_slip = fields.Boolean(
"Auto Print Delivery Slip",
help="If this checkbox is ticked, Odoo will automatically print the delivery slip of a picking when it is validated.")
count_picking_draft = fields.Integer(compute='_compute_picking_count')
count_picking_ready = fields.Integer(compute='_compute_picking_count')
@@ -1150,7 +1154,8 @@ class Picking(models.Model):
pickings_to_backorder = self - pickings_not_to_backorder
pickings_not_to_backorder.with_context(cancel_backorder=True)._action_done()
pickings_to_backorder.with_context(cancel_backorder=False)._action_done()
report_actions = self._get_autoprint_report_actions()
another_action = False
if self.user_has_groups('stock.group_reception_report'):
pickings_show_report = self.filtered(lambda p: p.picking_type_id.auto_show_reception_report)
lines = pickings_show_report.move_ids.filtered(lambda m: m.product_id.type == 'product' and m.state != 'cancel' and m.quantity_done and not m.move_dest_ids)
@@ -1166,7 +1171,18 @@ class Picking(models.Model):
('product_id', 'in', lines.product_id.ids)], limit=1):
action = pickings_show_report.action_view_reception_report()
action['context'] = {'default_picking_ids': pickings_show_report.ids}
return action
if not report_actions:
return action
another_action = action
if report_actions:
return {
'type': 'ir.actions.client',
'tag': 'do_multi_print',
'params': {
'reports': report_actions,
'anotherAction': another_action,
}
}
return True
def action_set_quantities_to_reservation(self):
@@ -1690,3 +1706,12 @@ class Picking(models.Model):
def _get_report_lang(self):
return self.move_ids and self.move_ids[0].partner_id.lang or self.partner_id.lang or self.env.lang
def _get_autoprint_report_actions(self):
report_actions = []
pickings_to_print = self.filtered(lambda p: p.picking_type_id.auto_print_delivery_slip)
if pickings_to_print:
action = self.env.ref("stock.action_report_delivery").report_action(pickings_to_print.ids, config=False)
clean_action(action, self.env)
report_actions.append(action)
return report_actions
@@ -0,0 +1,38 @@
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
import { sprintf } from "@web/core/utils/strings";
async function doMultiPrint(env, action) {
for (const report of action.params.reports) {
if (report.type != "ir.actions.report") {
env.services.notification.add(_t("Incorrect type of action submitted as a report, skipping action"), {
title: _t("Report Printing Error"),
});
continue
} else if (report.report_type === "qweb-html") {
env.services.notification.add(
sprintf(
_t("HTML reports cannot be auto-printed, skipping report: %s"),
report.name)
, {
title: _t("Report Printing Error"),
});
continue
}
// WARNING: potential issue if pdf generation fails, then action_service defaults
// to HTML and rest of the action chain will break w/potentially never resolving promise
await env.services.action.doAction({ type: "ir.actions.report", ...report });
}
if (action.params.anotherAction) {
return env.services.action.doAction(action.params.anotherAction);
} else if (action.params.on_close) {
// handle special cases such as barcode
action.params.on_close()
} else {
return env.services.action.doAction("reload_context");
}
}
registry.category("actions").add("do_multi_print", doMultiPrint);
+65 -43
View File
@@ -78,49 +78,71 @@
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
<label for="name"/>
<h1><field name="name" placeholder="e.g. Receptions"/></h1>
<group name="first">
<group>
<field name="code"/>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="hide_reservation_method" invisible="1"/>
<field name="show_picking_type" invisible="1"/>
<field name="sequence_id" groups="base.group_no_one"/>
<field name="sequence_code"/>
<field name="warehouse_id" groups="stock.group_stock_multi_warehouses" force_save="1"/>
<field name="reservation_method" invisible="hide_reservation_method" widget="radio"/>
<label for="reservation_days_before" string="Reserve before scheduled date" invisible="code == 'incoming' or reservation_method != 'by_date'"/>
<div class="o_row" invisible="code == 'incoming' or reservation_method != 'by_date'">
<span><field name="reservation_days_before" style="width: 23px;"/> days before/</span>
<span><field name="reservation_days_before_priority" style="width: 23px;"/> days before when starred</span>
</div>
<field name="auto_show_reception_report" invisible="code not in ['incoming', 'internal']" groups="stock.group_reception_report"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field invisible="code not in ['incoming', 'outgoing', 'internal']" name="return_picking_type_id" string="Returns Type"/>
<field name="default_location_return_id" invisible="code not in ['incoming', 'outgoing', 'internal']" groups="stock.group_stock_multi_locations"/>
<field name="create_backorder"/>
<field name="show_operations" invisible="not show_picking_type"/>
<field name="show_reserved" invisible="code != 'incoming'"/>
</group>
</group>
<group name="second">
<group invisible="code not in ['incoming', 'outgoing', 'internal']" string="Lots/Serial Numbers" groups="stock.group_production_lot" name="stock_picking_type_lot">
<field name="use_create_lots" string="Create New"/>
<field name="use_existing_lots" string="Use Existing ones"/>
</group>
<group invisible="code not in ['incoming', 'outgoing', 'internal']" string="Packages" groups="stock.group_tracking_lot">
<field name="show_entire_packs"/>
</group>
<!-- As this group will be hidden without multi_loccation, you will not be able to create a
picking type with the code 'Internal', which make sense, but as the field 'code' on picking
types can't be partially hidden, you can still select the code internal in the form view -->
<group string="Locations" groups="stock.group_stock_multi_locations" name="locations">
<field name="default_location_src_id" options="{'no_create': True}" required="code in ('internal', 'outgoing')"/>
<field name="default_location_dest_id" options="{'no_create': True}" required="code in ('internal', 'incoming')"/>
</group>
</group>
<notebook>
<page name="general" string="General">
<group name="first">
<group>
<field name="code"/>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="hide_reservation_method" invisible="1"/>
<field name="show_picking_type" invisible="1"/>
<field name="sequence_id" groups="base.group_no_one"/>
<field name="sequence_code"/>
<field name="warehouse_id" groups="stock.group_stock_multi_warehouses" force_save="1"/>
<field name="reservation_method" invisible="hide_reservation_method" widget="radio"/>
<label for="reservation_days_before" string="Reserve before scheduled date" invisible="code == 'incoming' or reservation_method != 'by_date'"/>
<div class="o_row" invisible="code == 'incoming' or reservation_method != 'by_date'">
<span><field name="reservation_days_before" style="width: 23px;"/> days before/</span>
<span><field name="reservation_days_before_priority" style="width: 23px;"/> days before when starred</span>
</div>
<field name="auto_show_reception_report" invisible="code not in ['incoming', 'internal']" groups="stock.group_reception_report"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field invisible="code not in ['incoming', 'outgoing', 'internal']" name="return_picking_type_id" string="Returns Type"/>
<field name="default_location_return_id" invisible="code not in ['incoming', 'outgoing', 'internal']" groups="stock.group_stock_multi_locations"/>
<field name="create_backorder"/>
<field name="show_operations" invisible="not show_picking_type"/>
<field name="show_reserved" invisible="code != 'incoming'"/>
</group>
</group>
<group name="second">
<group invisible="code not in ['incoming', 'outgoing', 'internal']" string="Lots/Serial Numbers" groups="stock.group_production_lot" name="stock_picking_type_lot">
<field name="use_create_lots" string="Create New"/>
<field name="use_existing_lots" string="Use Existing ones"/>
</group>
<group invisible="code not in ['incoming', 'outgoing', 'internal']" string="Packages" groups="stock.group_tracking_lot">
<field name="show_entire_packs"/>
</group>
<!-- As this group will be hidden without multi_loccation, you will not be able to create a
picking type with the code 'Internal', which make sense, but as the field 'code' on picking
types can't be partially hidden, you can still select the code internal in the form view -->
<group string="Locations" groups="stock.group_stock_multi_locations" name="locations">
<field name="default_location_src_id" options="{'no_create': True}" required="code in ('internal', 'outgoing')"/>
<field name="default_location_dest_id" options="{'no_create': True}" required="code in ('internal', 'incoming')"/>
</group>
</group>
</page>
<page name="hardware" string="Hardware" invisible="code not in ['incoming', 'outgoing', 'internal']" >
<group name="auto_print">
<group string="Print on Validation">
<field name="auto_print_delivery_slip" string="Delivery Slip"/>
</group>
<group string="">
<div colspan="2">
Odoo opens a PDF preview by default. If you (Enterprise users only) want to print instantly,
install the IoT App on a computer that is on the same local network as the
barcode operator and configure the routing of the reports.
<a href="https://www.odoo.com/documentation/master/applications/productivity/iot/devices/printer.html"
target="_blank" class="fa fa-arrow-right">
Documentation
</a>
</div>
</group>
</group>
</page>
</notebook>
</sheet>
</form>
</field>