新增主生产计划模块

This commit is contained in:
qihao.gong@jikimo.com
2023-08-15 10:36:04 +08:00
parent 4a5fb0c6e4
commit 1533ef7be9
72 changed files with 25769 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
/** @odoo-module **/
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
const { Component } = owl;
export class GroupMenu extends Component {
get items() {
return this.props.items;
}
_toggle_group(group) {
const value = {};
value[group] = !this.props.items[group];
this.env.model._saveCompanySettings(value);
}
}
GroupMenu.template = "mrp_mps.GroupMenu";
GroupMenu.components = { Dropdown, DropdownItem };

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="mrp_mps.GroupMenu" owl="1">
<Dropdown togglerClass="'btn btn-light'">
<t t-set-slot="toggler">
<i class="me-1" t-att-class="icon"/>
<span class="dropdown-toggle o_dropdown_title">Rows</span>
</t>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_starting_inventory }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_starting_inventory')">
Starting Inventory
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_demand_forecast }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_demand_forecast')">
Demand Forecast
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_actual_demand }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_actual_demand')">
Actual Demand
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_actual_demand_year_minus_1 }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_actual_demand_year_minus_1')">
Actual Demand Y-1
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_actual_demand_year_minus_2 }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_actual_demand_year_minus_2')">
Actual Demand Y-2
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_indirect_demand }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_indirect_demand')">
Indirect Demand Forecast
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_to_replenish }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_to_replenish')">
To Replenish
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_actual_replenishment }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_actual_replenishment')">
Actual Replenishment
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_safety_stock }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_safety_stock')">
Forecasted Stock
</DropdownItem>
<DropdownItem
class="{ o_menu_item: true, selected: items.mrp_mps_show_available_to_promise }"
parentClosingMode="'none'"
onSelected="() => this._toggle_group('mrp_mps_show_available_to_promise')">
Available to Promise
</DropdownItem>
</Dropdown>
</t>
</templates>

View File

@@ -0,0 +1,13 @@
/** @odoo-module **/
import { ImportRecords } from "@base_import/import_records/import_records";
import { registry } from "@web/core/registry";
const favoriteMenuRegistry = registry.category("favoriteMenu");
const mpsImportRecordsItem = {
Component: ImportRecords,
groupNumber: 4,
isDisplayed: ({ config }) =>
config.mpsImportRecords
};
favoriteMenuRegistry.add("mps-import-records-menu", mpsImportRecordsItem, { sequence: 1 });

View File

@@ -0,0 +1,139 @@
/** @odoo-module **/
import { GroupMenu } from "./group_menu";
import { download } from "@web/core/network/download";
import { useService } from "@web/core/utils/hooks";
import { ActionMenus } from "@web/search/action_menus/action_menus";
import { ControlPanel } from "@web/search/control_panel/control_panel";
import { ExportDataDialog } from "@web/views/view_dialogs/export_data_dialog";
export class MrpMpsControlPanel extends ControlPanel {
setup() {
super.setup();
this.rpc = useService("rpc");
this.dialogService = useService("dialog");
}
get model() {
return this.env.model;
}
get groups() {
return this.env.model.data.groups[0];
}
get isRecordSelected() {
return this.model.selectedRecords.size > 0;
}
getActionMenuItems() {
return Object.assign({}, {
other: [{
key: "export",
description: this.env._t("Export"),
callback: () => this.onExportData(),
}, {
key: "delete",
description: this.env._t("Delete"),
callback: () => this.unlinkSelectedRecord(),
}, {
key: "replenish",
description: this.env._t("Replenish"),
callback: () => this.replenishSelectedRecords(),
}]
});
}
/**
* Handles the click on replenish button. It will call action_replenish with
* all the Ids present in the view.
*
* @private
* @param {MouseEvent} ev
*/
_onClickReplenish(ev) {
this.env.model.replenishAll();
}
_onMouseOverReplenish(ev) {
this.model.mouseOverReplenish()
}
_onMouseOutReplenish(ev) {
this.model.mouseOutReplenish()
}
_onClickCreate(ev) {
this.env.model._createProduct();
}
replenishSelectedRecords() {
this.env.model.replenishSelectedRecords();
}
unlinkSelectedRecord() {
this.env.model.unlinkSelectedRecord();
}
async getExportedFields(model, import_compat, parentParams) {
return await this.rpc("/web/export/get_fields", {
...parentParams,
model,
import_compat,
});
}
async downloadExport(fields, import_compat, format) {
const resIds = Array.from(this.model.selectedRecords);
const exportedFields = fields.map((field) => ({
name: field.name || field.id,
label: field.label || field.string,
store: field.store,
type: field.field_type,
}));
if (import_compat) {
exportedFields.unshift({ name: "id", label: this.env._t("External ID") });
}
await download({
data: {
data: JSON.stringify({
import_compat,
context: this.props.context,
domain: this.model.domain,
fields: exportedFields,
ids: resIds.length > 0 && resIds,
model: "mrp.production.schedule",
}),
},
url: `/web/export/${format}`,
});
}
/**
* Opens the Export Dialog
*
* @private
*/
onExportData() {
const resIds = Array.from(this.model.selectedRecords);
const dialogProps = {
resIds,
context: this.props.context,
download: this.downloadExport.bind(this),
getExportedFields: this.getExportedFields.bind(this),
root: {
resModel: "mrp.production.schedule",
activeFields: [],
},
};
this.dialogService.add(ExportDataDialog, dialogProps);
}
}
MrpMpsControlPanel.template = "mrp_mps.MrpMpsControlPanel";
MrpMpsControlPanel.components = {
...ControlPanel.components,
ActionMenus,
GroupMenu,
};

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="mrp_mps.MrpMpsControlPanel.Regular" t-inherit="web.ControlPanel.Regular" t-inherit-mode="primary" owl="1">
<xpath expr="//t[@t-slot='control-panel-bottom-left']" position="replace">
<div>
<button
type="button"
class="btn btn-primary"
t-on-click.stop="_onClickReplenish"
t-on-mouseover.stop="_onMouseOverReplenish"
t-on-mouseout.stop="_onMouseOutReplenish">Replenish</button>
<button
type="button"
class='btn btn-secondary'
t-on-click.stop="_onClickCreate">Add a Product</button>
</div>
<ActionMenus t-if="isRecordSelected"
getActiveIds="() => Array.from(model.selectedRecords)"
context="{}"
domain="model.domain"
items="getActionMenuItems()"
isDomainSelected="model.isDomainSelected"
resModel="'mrp.production.schedule'"/>
</xpath>
<xpath expr="//t[@t-foreach='searchMenus']" position="before">
<GroupMenu items="groups"/>
</xpath>
</t>
<t t-name="mrp_mps.MrpMpsControlPanel" t-inherit="web.ControlPanel" t-inherit-mode="primary" owl="1">
<xpath expr="//t[@t-call='web.ControlPanel.Regular']" position="replace">
<t t-call="mrp_mps.MrpMpsControlPanel.Regular"/>
</xpath>
</t>
</templates>

View File

@@ -0,0 +1,28 @@
/** @odoo-module **/
import { Domain } from "@web/core/domain";
import { SearchModel } from "@web/search/search_model";
export class MrpMpsSearchModel extends SearchModel {
/**
* When search with field bom_id, also show components of that bom
*
* @override
*/
_getFieldDomain(field, autocompleteValues) {
let domain = super._getFieldDomain(...arguments);
if (field.fieldName === "bom_id") {
const additionalDomain = [
"|",
["product_id.bom_line_ids.bom_id", "ilike", autocompleteValues[0].value],
"|",
["product_id.variant_bom_ids", "ilike", autocompleteValues[0].value],
"&",
["product_tmpl_id.bom_ids.product_id", "=", false],
["product_tmpl_id.bom_ids", "ilike", autocompleteValues[0].value],
];
domain = Domain.or([additionalDomain, domain.toList()]);
}
return domain;
}
};