[REF] stock: reception_report into WOWL
Converts the legacy reception report into WOWL. Fixes 'Assign All' buttons disappearing from the report until it is reloaded when clicking on them. Removes references to 'report_type == html' from xml since it is only used as PDF now. Part of the global conversion of stock to WOWL. Task-2885757 closes odoo/odoo#102687 X-original-commit: f310d8f16b57c776ad92406bd52daa707ad45a88 Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com> Signed-off-by: Quentin Wolfs (quwo) <quwo@odoo.com>
This commit is contained in:
@@ -11,6 +11,16 @@ class ReceptionReport(models.AbstractModel):
|
||||
_name = 'report.stock.report_reception'
|
||||
_description = "Stock Reception Report"
|
||||
|
||||
@api.model
|
||||
def get_report_data(self, docids, data):
|
||||
report_values = self._get_report_values(docids, data)
|
||||
report_values['docs'] = self._format_html_docs(report_values.get('docs', False))
|
||||
report_values['sources_info'] = self._format_html_sources_info(report_values.get('sources_to_lines', {}))
|
||||
report_values['sources_to_lines'] = self._format_html_sources_to_lines(report_values.get('sources_to_lines', {}))
|
||||
report_values['sources_to_formatted_scheduled_date'] = self._format_html_sources_to_date(report_values.get('sources_to_formatted_scheduled_date', {}))
|
||||
report_values['show_uom'] = self.env.user.has_group('uom.group_uom')
|
||||
return report_values
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
''' This report is flexibly designed to work with both individual and batch pickings.
|
||||
@@ -325,3 +335,42 @@ class ReceptionReport(models.AbstractModel):
|
||||
def _action_unassign(self, in_move, out_move):
|
||||
""" For extension purposes only """
|
||||
return
|
||||
|
||||
def _format_html_docs(self, docs):
|
||||
""" Format docs to be sent in an html request. """
|
||||
return [{
|
||||
'id': doc.id,
|
||||
'name': doc.display_name,
|
||||
'state': doc.state,
|
||||
'display_state': dict(doc._fields['state']._description_selection(self.env)).get(doc.state),
|
||||
} for doc in docs] if docs else docs
|
||||
|
||||
def _format_html_sources_to_date(self, sources_to_dates):
|
||||
""" Format sources_to_formatted_scheduled_date to be sent in an html request. """
|
||||
return {str(source): date for (source, date) in sources_to_dates.items()}
|
||||
|
||||
def _format_html_sources_to_lines(self, sources_to_lines):
|
||||
""" Format sources_to_lines to be sent in an html request, while adding an index for OWL's t-foreach. """
|
||||
return {
|
||||
str(source): [{**line, 'index': i, 'move_out_id': line['move_out'].id} for i, line in enumerate(lines)]
|
||||
for source, lines in sources_to_lines.items()
|
||||
}
|
||||
|
||||
def _format_html_sources_info(self, sources_to_lines):
|
||||
""" Format used info from sources of sources_to_lines to be sent in an html request. """
|
||||
return {str(source): [self._format_html_source(s, index == 0) for index, s in enumerate(source)] for source in sources_to_lines.keys()}
|
||||
|
||||
def _format_html_source(self, source, is_picking=False):
|
||||
""" Format used info from a single source to be sent in an html request. """
|
||||
formatted = {
|
||||
'id': source.id,
|
||||
'model': source._name,
|
||||
'name': source.display_name,
|
||||
}
|
||||
if is_picking:
|
||||
formatted.update({
|
||||
'priority': source.priority,
|
||||
'partner_id': source.partner_id.id if source.partner_id else False,
|
||||
'partner_name': source.partner_id.name if source.partner_id else False,
|
||||
})
|
||||
return formatted
|
||||
|
||||
@@ -59,9 +59,9 @@
|
||||
</t>
|
||||
</h3>
|
||||
</div>
|
||||
<div><table t-if="sources_to_lines" class="o_report_reception_table table table-sm">
|
||||
<table t-if="sources_to_lines" class="o_report_reception_table table table-sm">
|
||||
<t t-foreach="sources_to_lines" t-as="source">
|
||||
<thead t-if="report_type == 'html' or any(line['is_assigned'] for line in sources_to_lines[source])">
|
||||
<thead t-if="any(line['is_assigned'] for line in sources_to_lines[source])">
|
||||
<tr class="bg-light">
|
||||
<th>
|
||||
<i t-if="source[0].priority == '1'" class="o_priority o_priority_star fa fa-star"/>
|
||||
@@ -78,71 +78,19 @@
|
||||
</span>
|
||||
</th>
|
||||
<th>Expected Delivery: <t t-esc="sources_to_formatted_scheduled_date[source]"/></th>
|
||||
<th t-if="report_type == 'html' and any(s['move_ins'] for s in sources_to_lines[source])">
|
||||
<button t-if="any(not s['is_assigned'] and s['is_qty_assignable'] for s in sources_to_lines[source])"
|
||||
class="btn btn-sm btn-primary o_report_reception_assign o_assign_all"
|
||||
name="assign_source_link">
|
||||
Assign All
|
||||
</button>
|
||||
</th>
|
||||
<th t-if="report_type == 'html'">
|
||||
<button t-if="any(s['move_ins'] for s in sources_to_lines[source])"
|
||||
class="btn btn-sm btn-primary o_print_label o_print_label_all"
|
||||
t-att-disabled="not any(s['is_assigned'] for s in sources_to_lines[source])"
|
||||
name="print_labels">
|
||||
<span class="d-none d-sm-block o_print_label_text">Print Labels</span>
|
||||
<span class="d-block d-sm-none fa fa-print"/>
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-set="possible_moves" t-value="sources_to_lines[source]"/>
|
||||
<t t-foreach="possible_moves" t-as="line">
|
||||
<!-- don't print non-assigned lines when printing pdf -->
|
||||
<tr t-if="report_type == 'html' or line['is_assigned']">
|
||||
<tr t-if="line['is_assigned']">
|
||||
<td>
|
||||
<t t-esc="line['product']['display_name']"/>
|
||||
</td>
|
||||
<td>
|
||||
<t t-esc="line['quantity']"/>
|
||||
<t groups="uom.group_uom" t-esc="line['uom']"/>
|
||||
<button t-if="report_type == 'html'"
|
||||
class="o_report_reception_forecasted btn btn-link fa fa-area-chart"
|
||||
t-att-move-id="line['move_out'].id"
|
||||
name="forecasted_report_link">
|
||||
</button>
|
||||
</td>
|
||||
<td t-if="report_type == 'html' and line['is_qty_assignable']">
|
||||
<button t-if="not line['is_assigned']"
|
||||
class="btn btn-sm btn-primary o_report_reception_assign"
|
||||
t-attf-model="stock.move"
|
||||
t-att-move-id="line['move_out'].id"
|
||||
t-att-move-ins-ids="line['move_ins']"
|
||||
t-att-qty="line['quantity']"
|
||||
name="assign_link">
|
||||
Assign
|
||||
</button>
|
||||
<button t-if="line['is_assigned']"
|
||||
class="btn btn-sm btn-primary o_report_reception_unassign"
|
||||
t-attf-model="stock.move"
|
||||
t-att-move-id="line['move_out'].id"
|
||||
t-att-move-ins-ids="line['move_ins']"
|
||||
t-att-qty="line['quantity']"
|
||||
name="unassign_link">
|
||||
Unassign
|
||||
</button>
|
||||
</td>
|
||||
<td t-if="report_type == 'html'">
|
||||
<button t-if="line['is_qty_assignable'] and line['source']"
|
||||
t-att-move-id="line['move_out'].id"
|
||||
t-att-qty="line['quantity']"
|
||||
t-attf-class="btn btn-sm btn-primary o_print_label"
|
||||
t-att-disabled="not line['is_assigned']"
|
||||
name="print_label">
|
||||
<span class="d-none d-sm-block o_print_label_text">Print Label</span>
|
||||
<span class="d-block d-sm-none fa fa-print"/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
@@ -151,16 +99,13 @@
|
||||
</table>
|
||||
<p t-else="">
|
||||
No allocation need found.
|
||||
</p></div>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="report_reception">
|
||||
<t t-call="web.html_container">
|
||||
<t t-if="report_type == 'pdf'" t-call="web.internal_layout">
|
||||
<t t-call="stock.report_reception_body"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-call="web.internal_layout">
|
||||
<t t-call="stock.report_reception_body"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/** @odoo-module **/
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { formatFloat } from "@web/views/fields/formatters";
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
export class ReceptionReportLine extends Component {
|
||||
setup() {
|
||||
this.ormService = useService("orm");
|
||||
this.actionService = useService("action");
|
||||
this.formatFloat = (val) => formatFloat(val, { digits: [false, this.props.precision] });
|
||||
}
|
||||
|
||||
//---- Handlers ----
|
||||
|
||||
async onClickForecast() {
|
||||
const action = await this.ormService.call(
|
||||
"stock.move",
|
||||
"action_product_forecast_report",
|
||||
[[this.data.move_out_id]],
|
||||
);
|
||||
|
||||
return this.actionService.doAction(action);
|
||||
}
|
||||
|
||||
async onClickPrint() {
|
||||
if (!this.data.move_out_id) {
|
||||
return;
|
||||
}
|
||||
const reportFile = 'stock.report_reception_report_label';
|
||||
const modelIds = [this.data.move_out_id];
|
||||
const productQtys = [Math.ceil(this.data.quantity) || '1'];
|
||||
|
||||
return this.actionService.doAction({
|
||||
type: "ir.actions.report",
|
||||
report_type: "qweb-pdf",
|
||||
report_name: `${reportFile}?docids=${modelIds}&quantity=${productQtys}`,
|
||||
report_file: reportFile,
|
||||
});
|
||||
}
|
||||
|
||||
async onClickAssign() {
|
||||
await this.ormService.call(
|
||||
"report.stock.report_reception",
|
||||
"action_assign",
|
||||
[false, [this.data.move_out_id], [this.data.quantity], [this.data.move_ins]],
|
||||
);
|
||||
this.env.bus.trigger("update-assign-state", { isAssigned: true, tableIndex: this.props.parentIndex, lineIndex: this.data.index });
|
||||
}
|
||||
|
||||
async onClickUnassign() {
|
||||
const done = await this.ormService.call(
|
||||
"report.stock.report_reception",
|
||||
"action_unassign",
|
||||
[false, this.data.move_out_id, this.data.quantity, this.data.move_ins]
|
||||
)
|
||||
if (done) {
|
||||
this.env.bus.trigger("update-assign-state", { isAssigned: false, tableIndex: this.props.parentIndex, lineIndex: this.data.index });
|
||||
}
|
||||
}
|
||||
|
||||
//---- Getters ----
|
||||
|
||||
get data() {
|
||||
return this.props.data;
|
||||
}
|
||||
}
|
||||
|
||||
ReceptionReportLine.template = "stock.ReceptionReportLine";
|
||||
ReceptionReportLine.props = {
|
||||
data: Object,
|
||||
parentIndex: String,
|
||||
showUom: Boolean,
|
||||
precision: Number,
|
||||
};
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<tr t-name="stock.ReceptionReportLine" class="align-middle" owl="1">
|
||||
<td t-esc="data.product.display_name"/>
|
||||
<td>
|
||||
<t t-esc="formatFloat(data.quantity)"/> <t t-if="props.showUom" t-esc="data.uom"/>
|
||||
<button class="btn btn-link fa fa-area-chart" t-on-click="onClickForecast" name="forecasted_report_link"/>
|
||||
</td>
|
||||
<td t-if="data.is_qty_assignable">
|
||||
<button t-if="!data.is_assigned"
|
||||
t-on-click="onClickAssign"
|
||||
class="btn btn-sm btn-primary"
|
||||
name="assign_link">
|
||||
Assign
|
||||
</button>
|
||||
<button t-else=""
|
||||
t-on-click="onClickUnassign"
|
||||
class="btn btn-sm btn-primary"
|
||||
name="unassign_link">
|
||||
Unassign
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button t-if="data.is_qty_assignable && data.source"
|
||||
class="btn btn-sm btn-primary"
|
||||
t-attf-disabled="{{ !data.is_assigned }}"
|
||||
name="print_label"
|
||||
t-on-click="onClickPrint">
|
||||
<span class="d-sm-block">Print Label</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</templates>
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useBus, useService } from "@web/core/utils/hooks";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { ReceptionReportTable } from "../reception_report_table/stock_reception_report_table";
|
||||
|
||||
const { Component, onWillStart, useState } = owl;
|
||||
|
||||
export class ReceptionReportMain extends Component {
|
||||
setup() {
|
||||
this.controlPanelDisplay = {
|
||||
"top-right": false,
|
||||
"bottom-right": false,
|
||||
};
|
||||
this.ormService = useService("orm");
|
||||
this.actionService = useService("action");
|
||||
this.reportName = "stock.report_reception";
|
||||
this.state = useState({
|
||||
sourcesToLines: {},
|
||||
});
|
||||
useBus(this.env.bus, "update-assign-state", (ev) => this._changeAssignedState(ev.detail));
|
||||
|
||||
onWillStart(async () => {
|
||||
this.data = await this.getReportData();
|
||||
this.state.sourcesToLines = this.data.sources_to_lines;
|
||||
});
|
||||
}
|
||||
|
||||
async getReportData() {
|
||||
const args = [
|
||||
this.context.default_picking_ids,
|
||||
{ context: this.context, report_type: "html" },
|
||||
];
|
||||
return this.ormService.call(
|
||||
"report.stock.report_reception",
|
||||
"get_report_data",
|
||||
args,
|
||||
{ context: this.context }
|
||||
);
|
||||
}
|
||||
|
||||
//---- Handlers ----
|
||||
|
||||
async onClickAssignAll() {
|
||||
const moveIds = [];
|
||||
const quantities = [];
|
||||
const inIds = [];
|
||||
|
||||
for (const lines of Object.values(this.state.sourcesToLines)) {
|
||||
for (const line of lines) {
|
||||
if (line.is_assigned) continue;
|
||||
moveIds.push(line.move_out_id);
|
||||
quantities.push(line.quantity);
|
||||
inIds.push(line.move_ins);
|
||||
}
|
||||
}
|
||||
|
||||
await this.ormService.call(
|
||||
"report.stock.report_reception",
|
||||
"action_assign",
|
||||
[false, moveIds, quantities, inIds],
|
||||
);
|
||||
this._changeAssignedState({ isAssigned: true });
|
||||
}
|
||||
|
||||
async onClickTitle(docId) {
|
||||
return this.actionService.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
res_model: this.data.doc_model,
|
||||
res_id: docId,
|
||||
views: [[false, "form"]],
|
||||
target: "current",
|
||||
});
|
||||
}
|
||||
|
||||
onClickPrint() {
|
||||
return this.actionService.doAction({
|
||||
type: "ir.actions.report",
|
||||
report_type: "qweb-pdf",
|
||||
report_name: `${this.reportName}/${this.context.default_picking_ids.join(",")}`,
|
||||
report_file: this.reportName,
|
||||
});
|
||||
}
|
||||
|
||||
onClickPrintLabels() {
|
||||
const reportFile = 'stock.report_reception_report_label';
|
||||
const modelIds = [];
|
||||
const quantities = [];
|
||||
|
||||
for (const lines of Object.values(this.state.sourcesToLines)) {
|
||||
for (const line of lines) {
|
||||
if (!line.is_assigned) continue;
|
||||
modelIds.push(line.move_out_id);
|
||||
quantities.push(line.quantity || 1);
|
||||
}
|
||||
}
|
||||
if (!modelIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.actionService.doAction({
|
||||
type: "ir.actions.report",
|
||||
report_type: "qweb-pdf",
|
||||
report_name: `${reportFile}?docids=${modelIds}&quantity=${quantities}`,
|
||||
report_file: reportFile,
|
||||
});
|
||||
}
|
||||
|
||||
//---- Utils ----
|
||||
|
||||
_changeAssignedState(options) {
|
||||
const { isAssigned, tableIndex, lineIndex } = options;
|
||||
|
||||
for (const [tabIndex, lines] of Object.entries(this.state.sourcesToLines)) {
|
||||
if (tableIndex && tableIndex != tabIndex) continue;
|
||||
lines.forEach(line => {
|
||||
if (isNaN(lineIndex) || lineIndex == line.index) {
|
||||
line.is_assigned = isAssigned;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//---- Getters ----
|
||||
|
||||
get context() {
|
||||
return this.props.action.context;
|
||||
}
|
||||
|
||||
get hasContent() {
|
||||
return this.data.sources_to_lines && Object.keys(this.data.sources_to_lines).length > 0;
|
||||
}
|
||||
|
||||
get isAssignAllDisabled() {
|
||||
return Object.values(this.state.sourcesToLines).every(lines => lines.every(line => line.is_assigned));
|
||||
}
|
||||
|
||||
get isPrintLabelDisabled() {
|
||||
return Object.values(this.state.sourcesToLines).every(lines => lines.every(line => !line.is_assigned));
|
||||
}
|
||||
}
|
||||
|
||||
ReceptionReportMain.components = {
|
||||
ControlPanel,
|
||||
ReceptionReportTable,
|
||||
};
|
||||
ReceptionReportMain.template = "stock.ReceptionReportMain";
|
||||
|
||||
registry.category("actions").add("reception_report", ReceptionReportMain);
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<div t-name="stock.ReceptionReportMain" class="o_action" owl="1">
|
||||
<ControlPanel display="controlPanelDisplay">
|
||||
<t t-set-slot="control-panel-bottom-left-buttons">
|
||||
<div class="o_cp_buttons" role="toolbar" aria-label="Control panel buttons">
|
||||
<div class="d-inline-block">
|
||||
<button t-on-click="onClickPrint" type="button" class="btn btn-primary" title="Print">Print</button>
|
||||
<button t-on-click="onClickAssignAll" class="btn btn-secondary ms-1" t-att-disabled="isAssignAllDisabled">Assign All</button>
|
||||
<button t-on-click="onClickPrintLabels" class="btn btn-secondary ms-1" t-att-disabled="isPrintLabelDisabled">Print Labels</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</ControlPanel>
|
||||
<div class="o_report_reception container-fluid justify-content-between">
|
||||
<div class="o_report_reception_header my-4">
|
||||
<h1>
|
||||
<t t-if="data.docs">
|
||||
<div t-foreach="data.docs" t-as="doc" t-key="doc.id">
|
||||
<a href="#" t-on-click.prevent="() => this.onClickTitle(doc.id)" view-type="form" t-esc="doc.name"/>
|
||||
<span t-esc="doc.display_state" t-attf-class="ms-1 align-text-top badge rounded-pill bg-opacity-50 {{ doc.state == 'done' ? 'bg-success' : 'bg-info' }}"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-esc="data.reason"/>
|
||||
</t>
|
||||
</h1>
|
||||
</div>
|
||||
<t t-if="hasContent">
|
||||
<table class="table table-sm">
|
||||
<ReceptionReportTable
|
||||
t-foreach="state.sourcesToLines" t-as="source" t-key="source"
|
||||
index="source"
|
||||
scheduledDate="data.sources_to_formatted_scheduled_date[source]"
|
||||
lines="state.sourcesToLines[source]"
|
||||
source="data.sources_info[source]"
|
||||
showUom="data.show_uom"
|
||||
precision="data.precision"/>
|
||||
</table>
|
||||
</t>
|
||||
<p t-else="">
|
||||
No allocation need found.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</templates>
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { ReceptionReportLine } from "../reception_report_line/stock_reception_report_line";
|
||||
|
||||
const { Component } = owl;
|
||||
|
||||
export class ReceptionReportTable extends Component {
|
||||
setup() {
|
||||
this.actionService = useService("action");
|
||||
this.ormService = useService("orm");
|
||||
}
|
||||
|
||||
//---- Handlers ----
|
||||
|
||||
async onClickAssignAll() {
|
||||
const moveIds = [];
|
||||
const quantities = [];
|
||||
const inIds = [];
|
||||
for (const line of this.props.lines) {
|
||||
if (line.is_assigned) continue;
|
||||
moveIds.push(line.move_out_id);
|
||||
quantities.push(line.quantity);
|
||||
inIds.push(line.move_ins);
|
||||
}
|
||||
|
||||
await this.ormService.call(
|
||||
"report.stock.report_reception",
|
||||
"action_assign",
|
||||
[false, moveIds, quantities, inIds],
|
||||
);
|
||||
this.env.bus.trigger("update-assign-state", { isAssigned: true, tableIndex: this.props.index });
|
||||
}
|
||||
|
||||
async onClickLink(resModel, resId, viewType) {
|
||||
return this.actionService.doAction({
|
||||
type: "ir.actions.act_window",
|
||||
res_model: resModel,
|
||||
res_id: resId,
|
||||
views: [[false, viewType]],
|
||||
target: "current",
|
||||
});
|
||||
}
|
||||
|
||||
async onClickPrintLabels() {
|
||||
const reportFile = 'stock.report_reception_report_label';
|
||||
const modelIds = [];
|
||||
const quantities = [];
|
||||
for (const line of this.props.lines) {
|
||||
if (!line.is_assigned) continue;
|
||||
modelIds.push(line.move_out_id);
|
||||
quantities.push(line.quantity || 1);
|
||||
}
|
||||
if (!modelIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.actionService.doAction({
|
||||
type: "ir.actions.report",
|
||||
report_type: "qweb-pdf",
|
||||
report_name: `${reportFile}?docids=${modelIds}&quantity=${quantities}`,
|
||||
report_file: reportFile,
|
||||
});
|
||||
}
|
||||
|
||||
//---- Getters ----
|
||||
|
||||
get hasMovesIn() {
|
||||
return this.props.lines.some(line => line.move_ins && line.move_ins.length > 0);
|
||||
}
|
||||
|
||||
get hasAssignAllButton() {
|
||||
return this.props.lines.some(line => line.is_qty_assignable);
|
||||
}
|
||||
|
||||
get isAssignAllDisabled() {
|
||||
return this.props.lines.every(line => line.is_assigned);
|
||||
}
|
||||
|
||||
get isPrintLabelDisabled() {
|
||||
return this.props.lines.every(line => !line.is_assigned);
|
||||
}
|
||||
}
|
||||
|
||||
ReceptionReportTable.template = "stock.ReceptionReportTable";
|
||||
ReceptionReportTable.components = {
|
||||
ReceptionReportLine,
|
||||
};
|
||||
ReceptionReportTable.props = {
|
||||
index: String,
|
||||
scheduledDate: { type: String, optional: true },
|
||||
lines: Array,
|
||||
source: Array,
|
||||
showUom: Boolean,
|
||||
precision: Number,
|
||||
};
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="stock.ReceptionReportTable" owl="1">
|
||||
<thead>
|
||||
<tr class="bg-light">
|
||||
<th>
|
||||
<i t-if="props.source[0].priority == '1'" class="o_priority o_priority_star fa fa-star"/>
|
||||
<a href="#" t-on-click.prevent="() => this.onClickLink(props.source[0].model, props.source[0].id, 'form')" t-out="props.source[0].name"/>
|
||||
<span t-if="props.source.length > 1">
|
||||
(<a href="#" t-on-click.prevent="() => this.onClickLink(props.source[1].model, props.source[1].id, 'form')" t-out="props.source[1].name"/>)
|
||||
</span>
|
||||
<span t-if="props.source[0].model == 'stock.picking' and props.source[0].partner_id">:
|
||||
<a href="#" t-on-click.prevent="() => this.onClickLink('res.partner', props.source[0].partner_id, 'form')" t-out="props.source[0].partner_name"/>
|
||||
</span>
|
||||
</th>
|
||||
<th>Expected Delivery: <t t-esc="props.scheduledDate"/></th>
|
||||
<th t-if="hasMovesIn">
|
||||
<button t-if="hasAssignAllButton" t-on-click="onClickAssignAll" class="btn btn-sm btn-primary" t-att-disabled="isAssignAllDisabled" name="assign_source_link">
|
||||
Assign All
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button t-if="hasMovesIn" t-on-click="onClickPrintLabels" class="btn btn-sm btn-primary" t-att-disabled="isPrintLabelDisabled" name="print_labels">
|
||||
<span class="d-none d-sm-block">Print Labels</span>
|
||||
<span class="d-block d-sm-none fa fa-print"/>
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="props.lines" t-as="line" t-key="line.index">
|
||||
<ReceptionReportLine
|
||||
data="line"
|
||||
parentIndex="props.index"
|
||||
showUom="props.showUom"
|
||||
precision="props.precision"/>
|
||||
</t>
|
||||
</tbody>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,202 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import clientAction from 'report.client_action';
|
||||
import core from 'web.core';
|
||||
|
||||
const qweb = core.qweb;
|
||||
const _t = core._t;
|
||||
|
||||
const ReceptionReport = clientAction.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function (parent, action, options) {
|
||||
this._super(...arguments);
|
||||
this.context = Object.assign(action.context || {}, {
|
||||
active_ids: action.context.default_picking_ids,
|
||||
});
|
||||
this.report_name = `stock.report_reception`;
|
||||
this.report_url = `/report/html/${this.report_name}/?context=${JSON.stringify(this.context)}`;
|
||||
this._title = action.name;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
return Promise.all([
|
||||
this._super(...arguments),
|
||||
]).then(() => {
|
||||
this._renderButtons();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
on_attach_callback: function () {
|
||||
this._super();
|
||||
this.iframe.addEventListener("load",
|
||||
() => this._bindAdditionalActionHandlers(),
|
||||
{ once: true }
|
||||
);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Renders extra report buttons in control panel
|
||||
*/
|
||||
_renderButtons: function () {
|
||||
this.$buttons.append(qweb.render('reception_report_buttons', {}));
|
||||
this.$buttons.on('click', '.o_report_reception_assign', this._onClickAssign.bind(this));
|
||||
this.$buttons.on('click', '.o_print_label', this._onClickPrintLabel.bind(this));
|
||||
this.controlPanelProps.cp_content = {
|
||||
$buttons: this.$buttons,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind additional <button> action handlers
|
||||
*/
|
||||
_bindAdditionalActionHandlers: function () {
|
||||
const rr = $(this.iframe).contents().find('.o_report_reception');
|
||||
rr.on('click', '.o_report_reception_assign', this._onClickAssign.bind(this));
|
||||
rr.on('click', '.o_report_reception_unassign', this._onClickUnassign.bind(this));
|
||||
rr.on('click', '.o_report_reception_forecasted', this._onClickForecastReport.bind(this));
|
||||
rr.on('click', '.o_print_label', this._onClickPrintLabel.bind(this));
|
||||
},
|
||||
|
||||
|
||||
_switchButton: function (button) {
|
||||
button.innerText = button.innerText.includes(_t("Unassign")) ? _t("Assign") : _t("Unassign");
|
||||
button.name = button.name === 'assign_link' ? 'unassign_link' : 'assign_link';
|
||||
button.classList.toggle("o_report_reception_assign");
|
||||
button.classList.toggle("o_report_reception_unassign");
|
||||
},
|
||||
|
||||
/**
|
||||
* Assign the specified move(s)
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onClickAssign: function (ev) {
|
||||
const el = ev.currentTarget;
|
||||
const quantities = []; // incoming qty amounts to assign
|
||||
const moveIds = [];
|
||||
const inIds = [];
|
||||
let nodeToAssign = [];
|
||||
if (el.name === 'assign_link') { // One line "Assign"
|
||||
nodeToAssign = [el];
|
||||
el.closest('tbody').previousElementSibling.querySelectorAll('.o_print_label_all').forEach(button => button.removeAttribute('disabled'));
|
||||
} else {
|
||||
el.style.display = 'none';
|
||||
if (el.name === "assign_all_link") { // Global "Assign All"
|
||||
const iframe = this.iframe.contentDocument;
|
||||
iframe.querySelectorAll('.o_assign_all').forEach(button => button.style.display = 'none');
|
||||
iframe.querySelectorAll('.o_print_label_all').forEach(button => button.removeAttribute('disabled'));
|
||||
nodeToAssign = iframe.querySelectorAll('.o_report_reception_assign:not(.o_assign_all)');
|
||||
} else { // Local assign all
|
||||
nodeToAssign = el.closest('thead').nextElementSibling.querySelectorAll('.o_report_reception_assign:not(.o_assign_all)');
|
||||
const thead = el.closest('thead');
|
||||
thead.querySelector('.o_print_label_all').removeAttribute('disabled');
|
||||
thead.nextElementSibling.querySelectorAll('.o_print_label_all').forEach(button => button.removeAttribute('disabled'));
|
||||
}
|
||||
}
|
||||
nodeToAssign.forEach(node => {
|
||||
node.closest('td').nextElementSibling.querySelectorAll('.o_print_label').forEach(button => button.removeAttribute('disabled'));
|
||||
moveIds.push(parseInt(node.getAttribute('move-id')));
|
||||
quantities.push(parseFloat(node.getAttribute('qty')));
|
||||
inIds.push(JSON.parse(node.getAttribute('move-ins-ids')));
|
||||
this._switchButton(node);
|
||||
});
|
||||
|
||||
return this._rpc({
|
||||
model: 'report.stock.report_reception',
|
||||
args: [false, moveIds, quantities, inIds],
|
||||
method: 'action_assign'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Unassign the specified move
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onClickUnassign: function (ev) {
|
||||
const el = ev.currentTarget;
|
||||
const quantity = parseFloat(el.getAttribute('qty'));
|
||||
const modelId = parseInt(el.getAttribute('move-id'));
|
||||
const inIds = JSON.parse("[" + el.getAttribute('move-ins-ids') + "]");
|
||||
return this._rpc({
|
||||
model: 'report.stock.report_reception',
|
||||
args: [false, modelId, quantity, inIds[0]],
|
||||
method: 'action_unassign'
|
||||
}).then(() => {
|
||||
// only switch buttons if successful
|
||||
this._switchButton(el);
|
||||
el.closest('td').nextElementSibling.querySelectorAll('.o_print_label').forEach(button => button.setAttribute('disabled', true));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the forecast report for the product of the selected move.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onClickForecastReport: function (ev) {
|
||||
const modelId = parseInt(ev.currentTarget.getAttribute('move-id'));
|
||||
return this._rpc({
|
||||
model: 'stock.move',
|
||||
args: [[modelId]],
|
||||
method: 'action_product_forecast_report'
|
||||
}).then((action) => {
|
||||
return this.do_action(action);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Print the corresponding source label
|
||||
*/
|
||||
_onClickPrintLabel: function (ev) {
|
||||
const el = ev.currentTarget;
|
||||
const modelIds = [];
|
||||
const productQtys = [];
|
||||
const report_file = 'stock.report_reception_report_label';
|
||||
let nodeToPrint = [];
|
||||
|
||||
if (el.name === 'print_label') { // One line print
|
||||
nodeToPrint = [el];
|
||||
} else {
|
||||
if (el.name === "print_all_labels") { // Global "Print Labels"
|
||||
nodeToPrint = this.iframe.contentDocument.querySelectorAll('.o_print_label:not(.o_print_label_all):not(:disabled)');
|
||||
} else { // Local "Print Labels"
|
||||
nodeToPrint = el.closest('thead').nextElementSibling.querySelectorAll('.o_print_label:not(.o_print_label_all):not(:disabled)');
|
||||
}
|
||||
}
|
||||
|
||||
nodeToPrint.forEach(node => {
|
||||
modelIds.push(parseInt(node.getAttribute('move-id')));
|
||||
productQtys.push(Math.ceil(node.getAttribute('qty')) || '1');
|
||||
});
|
||||
|
||||
if (!modelIds.length) { // Nothing to print for this model.
|
||||
return Promise.resolve();
|
||||
}
|
||||
const report_name = `${report_file}?docids=${modelIds}&report_type=qweb-pdf&quantity=${productQtys}`;
|
||||
const action = {
|
||||
type: 'ir.actions.report',
|
||||
report_type: 'qweb-pdf',
|
||||
report_name,
|
||||
report_file,
|
||||
};
|
||||
return this.do_action(action);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
core.action_registry.add('reception_report', ReceptionReport);
|
||||
|
||||
export default ReceptionReport;
|
||||
Reference in New Issue
Block a user