Initial commit: Sale packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:49 +02:00
commit 14e3d26998
6469 changed files with 2479670 additions and 0 deletions

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-inherit="project.ProjectMilestone" t-inherit-mode="extension" owl="1">
<xpath expr="//t[@t-esc='milestone.name']" position="replace">
<span>
<t t-esc="milestone.name"/>
<span t-if="milestone.allow_billable &amp;&amp; milestone.quantity_percentage &amp;&amp; !milestone.sale_line_name" class="fst-italic text-muted">
(<t t-esc="(100 * milestone.quantity_percentage).toFixed(2)"/>%)
</span>
</span>
<span t-if="milestone.allow_billable" t-attf-class="fst-italic {{state.colorClass || 'text-muted'}} ms-2">
<t t-if="milestone.sale_line_name" t-esc="milestone.sale_line_name"/>
<span t-if="milestone.quantity_percentage &amp;&amp; milestone.sale_line_name">
(<t t-esc="(100 * milestone.quantity_percentage).toFixed(2)"/>%)
</span>
</span>
</xpath>
</t>
</templates>

View file

@ -0,0 +1,55 @@
/** @odoo-module */
import { patch } from '@web/core/utils/patch';
import { formatFloatTime, formatFloat } from "@web/views/fields/formatters";
import { ProjectRightSidePanel } from '@project/components/project_right_side_panel/project_right_side_panel';
patch(ProjectRightSidePanel.prototype, '@sale_project/components/project_right_side_panel/project_right_side_panel', {
async _loadAdditionalSalesOrderItems() {
const offset = this.state.data.sale_items.data.length;
const totalRecords = this.state.data.sale_items.total;
const limit = totalRecords - offset <= 5 ? totalRecords - offset : 5;
const saleOrderItems = await this.orm.call(
'project.project',
'get_sale_items_data',
[this.projectId, undefined, offset, limit],
{
context: this.context,
},
);
this.state.data.sale_items.data = [...this.state.data.sale_items.data, ...saleOrderItems];
},
async onLoadSalesOrderLinesClick() {
const saleItems = this.state.data.sale_items;
if (saleItems && saleItems.total > saleItems.data.length) {
await this._loadAdditionalSalesOrderItems();
}
},
formatValue(value, unit) {
return unit === 'Hours' ? formatFloatTime(value) : formatFloat(value);
},
//---------------------------------------------------------------------
// Handlers
//---------------------------------------------------------------------
/**
* @private
* @param {Object} params
*/
async onSaleItemActionClick(params) {
if (params.resId && params.type !== 'object') {
const action = await this.actionService.loadAction(params.name, this.context);
this.actionService.doAction({
...action,
res_id: params.resId,
views: [[false, 'form']]
});
} else {
this.onProjectActionClick(params);
}
},
});

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-name="sale_project.ProjectRightSidePanel" t-inherit="project.ProjectRightSidePanel" t-inherit-mode="extension" owl="1">
<xpath expr="//ProjectRightSidePanelSection[@name=&quot;'profitability'&quot;]" position="before">
<ProjectRightSidePanelSection
name="'sales'"
show="state.data.sale_items and state.data.sale_items.total &gt; 0"
>
<t t-set-slot="title" owl="1">
Sales
</t>
<table class="table table-striped table-hover mb-4">
<thead>
<tr>
<th>Sales Order Items</th>
<th class="text-end">Sold</th>
<th class="text-end">Delivered</th>
<th class="text-end">Invoiced</th>
</tr>
</thead>
<tbody>
<tr t-foreach="state.data.sale_items.data" t-as="sale_item" t-key="sale_item.id">
<t t-set="uom_name" t-value="sale_item.product_uom and sale_item.product_uom[1]"/>
<td>
<t t-set="sol_name" t-value="sale_item.display_name"/>
<a t-if="sale_item.action" class="o_rightpanel_button" href="#" t-on-click="() => this.onSaleItemActionClick(sale_item.action)">
<t t-esc="sol_name"/>
</a>
<t t-else="" t-esc="sol_name"/>
</td>
<td class="text-end align-middle"><t t-esc="formatValue(sale_item.product_uom_qty, uom_name)"/> <t t-esc="uom_name"/></td>
<td class="text-end align-middle"><t t-esc="formatValue(sale_item.qty_delivered, uom_name)"/> <t t-esc="uom_name"/></td>
<td class="text-end align-middle"><t t-esc="formatValue(sale_item.qty_invoiced, uom_name)"/> <t t-esc="uom_name"/></td>
</tr>
</tbody>
<tfoot>
<tr class="o_rightpanel_nohover border-0" t-if="state.data.sale_items.total &gt; state.data.sale_items.data.length">
<td class="pb-0 border-0 text-center" colspan="4">
<a class="btn btn-link" t-on-click="onLoadSalesOrderLinesClick">
Load more
</a>
</td>
</tr>
<tr t-else="">
<td class="pb-0 border-0 shadow-none text-center" colspan="4">
<small class="text-muted">All items have been loaded</small>
</td>
</tr>
</tfoot>
</table>
</ProjectRightSidePanelSection>
</xpath>
</t>
</templates>