Eliminate Python dependency: embed frontend assets in odoo-go
- Copy all OWL frontend assets (JS/CSS/XML/fonts/images) into frontend/ directory (2925 files, 43MB) — no more runtime reads from Python Odoo - Replace OdooAddonsPath config with FrontendDir pointing to local frontend/ - Rewire bundle.go, static.go, templates.go, webclient.go to read from frontend/ instead of external Python Odoo addons directory - Auto-detect frontend/ and build/ dirs relative to binary in main.go - Delete obsolete Python helper scripts (tools/*.py) The Go server is now fully self-contained: single binary + frontend/ folder. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { monetaryField, MonetaryField } from "@web/views/fields/monetary/monetary_field";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { floatIsZero } from "@web/core/utils/numbers";
|
||||
|
||||
export class MonetaryFieldNoZero extends MonetaryField {
|
||||
static props = {
|
||||
...MonetaryField.props,
|
||||
};
|
||||
|
||||
/** Override **/
|
||||
get value() {
|
||||
const originalValue = super.value;
|
||||
const decimals = this.currencyDigits ? this.currencyDigits[1] : 2;
|
||||
return floatIsZero(originalValue, decimals) ? false : originalValue;
|
||||
}
|
||||
}
|
||||
|
||||
export const monetaryFieldNoZero = {
|
||||
...monetaryField,
|
||||
component: MonetaryFieldNoZero,
|
||||
};
|
||||
|
||||
registry.category("fields").add("monetary_no_zero", monetaryFieldNoZero);
|
||||
@@ -0,0 +1,27 @@
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { standardFieldProps } from "@web/views/fields/standard_field_props";
|
||||
import { Component } from "@odoo/owl";
|
||||
|
||||
class OpenMatchLineWidget extends Component {
|
||||
static template = "purchase.OpenMatchLineWidget";
|
||||
static props = { ...standardFieldProps };
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.action = useService("action");
|
||||
}
|
||||
|
||||
async openMatchLine() {
|
||||
this.action.doActionButton({
|
||||
type: "object",
|
||||
resId: this.props.record.resId,
|
||||
name: "action_open_line",
|
||||
resModel: "purchase.bill.line.match",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("fields").add("open_match_line_widget", {
|
||||
component: OpenMatchLineWidget,
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
|
||||
<t t-name="purchase.OpenMatchLineWidget">
|
||||
<div t-out="props.record.data[props.name]" t-on-click.prevent.stop="openMatchLine"/>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { standardWidgetProps } from "@web/views/widgets/standard_widget_props";
|
||||
import { FileUploader } from "@web/views/fields/file_handler";
|
||||
import { WarningDialog } from "@web/core/errors/error_dialogs";
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
|
||||
export class PurchaseFileUploader extends Component {
|
||||
static template = "purchase.DocumentFileUploader";
|
||||
static props = {
|
||||
...standardWidgetProps,
|
||||
record: { type: Object, optional: true },
|
||||
list: { type: Object, optional: true },
|
||||
};
|
||||
static components = { FileUploader };
|
||||
|
||||
setup() {
|
||||
this.orm = useService("orm");
|
||||
this.action = useService("action");
|
||||
this.dialog = useService("dialog");
|
||||
this.attachmentIdsToProcess = [];
|
||||
}
|
||||
|
||||
get resModel() {
|
||||
return "purchase.order";
|
||||
}
|
||||
|
||||
get records() {
|
||||
return this.props.record ? [this.props.record] : this.props.list.records;
|
||||
}
|
||||
|
||||
async getIds() {
|
||||
if (this.props.record) {
|
||||
return this.props.record.data.id;
|
||||
}
|
||||
return this.props.list.getResIds(true);
|
||||
}
|
||||
|
||||
onClick(ev) {
|
||||
if (this.env.config.viewType !== "list") {
|
||||
return;
|
||||
}
|
||||
const vendorSet = new Set(this.props.list.selection.map((record) => record.data.partner_id.id));
|
||||
if (vendorSet.size > 1) {
|
||||
this.dialog.add(WarningDialog, {
|
||||
title: _t("Validation Error"),
|
||||
message: _t("You can only upload a bill for a single vendor at a time."),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async onFileUploaded(file) {
|
||||
const att_data = {
|
||||
name: file.name,
|
||||
mimetype: file.type,
|
||||
datas: file.data,
|
||||
};
|
||||
const [att_id] = await this.orm.create("ir.attachment", [att_data], {
|
||||
context: { ...this.env.searchModel.context },
|
||||
});
|
||||
this.attachmentIdsToProcess.push(att_id);
|
||||
}
|
||||
|
||||
async onUploadComplete() {
|
||||
const resModel = this.resModel;
|
||||
const ids = await this.getIds();
|
||||
let action;
|
||||
try {
|
||||
action = await this.orm.call(
|
||||
resModel,
|
||||
"action_create_invoice",
|
||||
[ids, this.attachmentIdsToProcess],
|
||||
{ context: { ...this.env.searchModel.context } }
|
||||
);
|
||||
} finally {
|
||||
// ensures attachments are cleared on success as well as on error
|
||||
this.attachmentIdsToProcess = [];
|
||||
}
|
||||
this.action.doAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
export const purchaseFileUploader = {
|
||||
component: PurchaseFileUploader,
|
||||
};
|
||||
|
||||
registry.category("view_widgets").add("purchase_file_uploader", purchaseFileUploader);
|
||||
@@ -0,0 +1,18 @@
|
||||
<templates>
|
||||
<t t-name="purchase.DocumentFileUploader">
|
||||
<FileUploader
|
||||
acceptedFileExtensions="props.acceptedFileExtensions"
|
||||
fileUploadClass="'document_file_uploader'"
|
||||
multiUpload="true"
|
||||
onClick.bind="onClick"
|
||||
onUploaded.bind="onFileUploaded"
|
||||
onUploadComplete.bind="onUploadComplete">
|
||||
<t t-set-slot="toggler">
|
||||
<button type="button" class="btn btn-secondary" data-hotkey="shift+i">
|
||||
Upload Bill
|
||||
</button>
|
||||
</t>
|
||||
<t t-slot="default"/>
|
||||
</FileUploader>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
<t t-inherit="account.TaxTotalsField" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//tr[2]" position="after">
|
||||
<tr t-if="totals.amount_total_cc">
|
||||
<td colspan="2">
|
||||
<span t-out="totals.amount_total_cc" style="font-size: 1.1em;"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
Reference in New Issue
Block a user