This commit is contained in:
mohammed-alkhazrji 2025-12-26 01:11:49 +03:00
parent fa01ac0106
commit 8a2020d893
2 changed files with 168 additions and 37 deletions

View File

@ -27,7 +27,7 @@ export class KpiFormulaField extends Component {
onWillStart(async () => { onWillStart(async () => {
try { try {
// Load draggabilly library // Load draggabilly library
await loadJS("kpi_scorecard/static/lib/draggabilly/draggabilly.pkgd.js"); await loadJS("/kpi_scorecard/static/lib/draggabilly/draggabilly.pkgd.js");
await this.loadFormulaData(); await this.loadFormulaData();
} catch (error) { } catch (error) {
console.error("Error loading KPI formula:", error); console.error("Error loading KPI formula:", error);
@ -51,12 +51,14 @@ export class KpiFormulaField extends Component {
async loadFormulaData() { async loadFormulaData() {
try { try {
const fieldValue = this.props.record.data[this.props.name] || "";
if (this.props.readonly) { if (this.props.readonly) {
// Load formula parts for readonly display // Load formula parts for readonly display
this.state.formulaParts = await this.orm.call( this.state.formulaParts = await this.orm.call(
"kpi.item", "kpi.item",
"action_render_formula", "action_render_formula",
[this.props.value || ""] [fieldValue]
); );
} else { } else {
// Load variables for edit mode // Load variables for edit mode
@ -65,13 +67,14 @@ export class KpiFormulaField extends Component {
this.state.variables = await this.orm.call( this.state.variables = await this.orm.call(
"kpi.item", "kpi.item",
"action_return_measures", "action_return_measures",
[[recordId], this.props.value || ""] [[recordId], fieldValue]
); );
} }
} }
this.state.isLoaded = true; this.state.isLoaded = true;
} catch (error) { } catch (error) {
console.error("Error loading formula data:", error); console.error("Error loading formula data:", error);
this.state.isLoaded = true; // Set to true even on error
} }
} }
@ -128,7 +131,8 @@ export class KpiFormulaField extends Component {
} }
}); });
this.props.update(formula.trim()); // Update using Odoo 18 method
this.props.record.update({ [this.props.name]: formula.trim() });
} }
onSearch(ev) { onSearch(ev) {
@ -176,5 +180,7 @@ export class KpiFormulaField extends Component {
} }
} }
// Register the field widget // ✅ الطريقة الصحيحة للتسجيل في Odoo 18
registry.category("fields").add("kpi_formula", KpiFormulaField); registry.category("fields").add("kpi_formula", {
component: KpiFormulaField,
});

View File

@ -2,14 +2,14 @@
<templates> <templates>
<t t-name="kpi_scorecard.KpiFormulaField"> <t t-name="kpi_scorecard.KpiFormulaField">
<div class="o_field_kpi_formula" t-ref="formula"> <div class="o_field_kpi_formula" t-ref="formula">
<!-- Readonly Mode -->
<t t-if="props.readonly"> <t t-if="props.readonly">
<div class="formula-readonly"> <div class="formula-readonly">
<t t-if="state.formulaParts and state.formulaParts.length"> <t t-if="state.formulaParts and state.formulaParts.length">
<t t-foreach="state.formulaParts" t-as="part" t-key="part_index"> <t t-foreach="state.formulaParts" t-as="part" t-key="part_index">
<span t-att-class="'formula-part ' + (part.type || '').toLowerCase()" <span t-att-class="'formula-part ' + (part.type || '').toLowerCase()"
t-att-title="part.name"> t-att-title="part.name"
<t t-esc="part.name"/> t-esc="part.name"/>
</span>
</t> </t>
</t> </t>
<t t-else=""> <t t-else="">
@ -17,51 +17,74 @@
</t> </t>
</div> </div>
</t> </t>
<!-- Edit Mode -->
<t t-else=""> <t t-else="">
<div class="formula-editor" t-if="state.isLoaded"> <div class="formula-editor" t-if="state.isLoaded">
<!-- Search Input -->
<div class="formula-search mb-2"> <div class="formula-search mb-2">
<input type="text" <input type="text"
class="kpi-search-input form-control" class="kpi-search-input form-control"
placeholder="Search..." placeholder="Search..."
t-on-keyup="onSearch"/> t-on-input="onSearch"/>
</div> </div>
<div class="row"> <div class="row">
<!-- Left Column: Variables and Operators -->
<div class="col-md-6"> <div class="col-md-6">
<!-- Variables Section -->
<div class="formula-variables" t-if="state.variables"> <div class="formula-variables" t-if="state.variables">
<t t-foreach="state.variables.sections || []" t-as="section" t-key="section_index"> <t t-if="state.variables.sections">
<t t-foreach="state.variables.sections" t-as="section" t-key="section_index">
<div class="variable-section mb-3"> <div class="variable-section mb-3">
<h6 t-esc="section.name" class="text-primary"/> <h6 t-esc="section.name" class="text-primary"/>
<div class="variable-items"> <div class="variable-items">
<t t-foreach="section.items || []" t-as="item" t-key="item_index"> <t t-if="section.items">
<t t-foreach="section.items" t-as="item" t-key="item_index">
<div class="formula-item draggable btn btn-sm btn-outline-secondary m-1" <div class="formula-item draggable btn btn-sm btn-outline-secondary m-1"
t-att-data-formula="item.formula" t-att-data-formula="item.formula"
t-att-data-part-id="item.id"> t-att-data-part-id="item.id">
<span t-esc="item.name"/> <span t-esc="item.name"/>
<button class="btn btn-sm btn-danger ms-1" <button class="btn btn-sm btn-danger ms-1"
t-on-click="() => onDeletePart(item.id)" t-on-click.stop="(ev) => this.onDeletePart(item.id)"
type="button"> type="button">
<i class="fa fa-times"/> <i class="fa fa-times"/>
</button> </button>
</div> </div>
</t> </t>
</t>
</div> </div>
</div> </div>
</t> </t>
</t>
</div> </div>
<!-- Operators Section -->
<div class="formula-operators mb-3"> <div class="formula-operators mb-3">
<h6 class="text-primary">Operators</h6> <h6 class="text-primary">Operators</h6>
<button t-foreach="['+', '-', '*', '/', '(', ')']" <div class="d-flex flex-wrap gap-1">
t-as="op" <button class="btn btn-secondary btn-sm"
t-key="op" t-on-click="() => this.onAddOperator('+')"
class="btn btn-secondary btn-sm m-1" type="button">+</button>
t-on-click="() => onAddOperator(op)" <button class="btn btn-secondary btn-sm"
type="button"> t-on-click="() => this.onAddOperator('-')"
<t t-esc="op"/> type="button">-</button>
</button> <button class="btn btn-secondary btn-sm"
t-on-click="() => this.onAddOperator('*')"
type="button">*</button>
<button class="btn btn-secondary btn-sm"
t-on-click="() => this.onAddOperator('/')"
type="button">/</button>
<button class="btn btn-secondary btn-sm"
t-on-click="() => this.onAddOperator('(')"
type="button">(</button>
<button class="btn btn-secondary btn-sm"
t-on-click="() => this.onAddOperator(')')"
type="button">)</button>
</div>
</div> </div>
<!-- Number Input Section -->
<div class="formula-number mb-3"> <div class="formula-number mb-3">
<h6 class="text-primary">Number</h6> <h6 class="text-primary">Number</h6>
<input type="number" <input type="number"
@ -71,6 +94,7 @@
</div> </div>
</div> </div>
<!-- Right Column: Formula Builder -->
<div class="col-md-6"> <div class="col-md-6">
<h6 class="text-primary">Formula Builder</h6> <h6 class="text-primary">Formula Builder</h6>
<div class="formula-drop-zone border rounded p-3 mb-3" <div class="formula-drop-zone border rounded p-3 mb-3"
@ -82,16 +106,117 @@
<label class="form-label">Current Formula:</label> <label class="form-label">Current Formula:</label>
<input type="text" <input type="text"
class="form-control" class="form-control"
t-att-value="props.value || ''" t-att-value="props.record.data[props.name] || ''"
readonly="readonly"/> readonly="readonly"/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Loading State -->
<div t-else="" class="text-center text-muted p-3"> <div t-else="" class="text-center text-muted p-3">
<i class="fa fa-spinner fa-spin me-2"/>
Loading formula editor... Loading formula editor...
</div> </div>
</t> </t>
</div> </div>
</t> </t>
</templates> </templates>
<!--<?xml version="1.0" encoding="UTF-8"?>-->
<!--<templates>-->
<!-- <t t-name="kpi_scorecard.KpiFormulaField">-->
<!-- <div class="o_field_kpi_formula" t-ref="formula">-->
<!-- <t t-if="props.readonly">-->
<!-- <div class="formula-readonly">-->
<!-- <t t-if="state.formulaParts and state.formulaParts.length">-->
<!-- <t t-foreach="state.formulaParts" t-as="part" t-key="part_index">-->
<!-- <span t-att-class="'formula-part ' + (part.type || '').toLowerCase()"-->
<!-- t-att-title="part.name">-->
<!-- <t t-esc="part.name"/>-->
<!-- </span>-->
<!-- </t>-->
<!-- </t>-->
<!-- <t t-else="">-->
<!-- <span class="text-muted">No formula defined</span>-->
<!-- </t>-->
<!-- </div>-->
<!-- </t>-->
<!-- <t t-else="">-->
<!-- <div class="formula-editor" t-if="state.isLoaded">-->
<!-- <div class="formula-search mb-2">-->
<!-- <input type="text"-->
<!-- class="kpi-search-input form-control"-->
<!-- placeholder="Search..."-->
<!-- t-on-keyup="onSearch"/>-->
<!-- </div>-->
<!-- <div class="row">-->
<!-- <div class="col-md-6">-->
<!-- <div class="formula-variables" t-if="state.variables">-->
<!-- <t t-foreach="state.variables.sections || []" t-as="section" t-key="section_index">-->
<!-- <div class="variable-section mb-3">-->
<!-- <h6 t-esc="section.name" class="text-primary"/>-->
<!-- <div class="variable-items">-->
<!-- <t t-foreach="section.items || []" t-as="item" t-key="item_index">-->
<!-- <div class="formula-item draggable btn btn-sm btn-outline-secondary m-1"-->
<!-- t-att-data-formula="item.formula"-->
<!-- t-att-data-part-id="item.id">-->
<!-- <span t-esc="item.name"/>-->
<!-- <button class="btn btn-sm btn-danger ms-1"-->
<!-- t-on-click="() => onDeletePart(item.id)"-->
<!-- type="button">-->
<!-- <i class="fa fa-times"/>-->
<!-- </button>-->
<!-- </div>-->
<!-- </t>-->
<!-- </div>-->
<!-- </div>-->
<!-- </t>-->
<!-- </div>-->
<!-- <div class="formula-operators mb-3">-->
<!-- <h6 class="text-primary">Operators</h6>-->
<!-- <button t-foreach="['+', '-', '*', '/', '(', ')']"-->
<!-- t-as="op"-->
<!-- t-key="op"-->
<!-- class="btn btn-secondary btn-sm m-1"-->
<!-- t-on-click="() => onAddOperator(op)"-->
<!-- type="button">-->
<!-- <t t-esc="op"/>-->
<!-- </button>-->
<!-- </div>-->
<!-- <div class="formula-number mb-3">-->
<!-- <h6 class="text-primary">Number</h6>-->
<!-- <input type="number"-->
<!-- class="kpi-number-input form-control"-->
<!-- placeholder="Enter number..."-->
<!-- t-on-change="onChangeNumber"/>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="col-md-6">-->
<!-- <h6 class="text-primary">Formula Builder</h6>-->
<!-- <div class="formula-drop-zone border rounded p-3 mb-3"-->
<!-- style="min-height: 100px; background-color: #f8f9fa;">-->
<!-- <small class="text-muted">Drag formula parts here</small>-->
<!-- </div>-->
<!-- <div class="current-formula">-->
<!-- <label class="form-label">Current Formula:</label>-->
<!-- <input type="text"-->
<!-- class="form-control"-->
<!-- t-att-value="props.value || ''"-->
<!-- readonly="readonly"/>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div t-else="" class="text-center text-muted p-3">-->
<!-- Loading formula editor...-->
<!-- </div>-->
<!-- </t>-->
<!-- </div>-->
<!-- </t>-->
<!--</templates>-->