diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 35410cac..00000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# 默认忽略的文件
-/shelf/
-/workspace.xml
-# 基于编辑器的 HTTP 客户端请求
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 105ce2da..00000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jikimo-sf.iml b/.idea/jikimo-sf.iml
deleted file mode 100644
index ea0ee661..00000000
--- a/.idea/jikimo-sf.iml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 9e0e1599..00000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 1ccf48a0..00000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7f..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/owl_demo/__init__.py b/owl_demo/__init__.py
new file mode 100644
index 00000000..f7209b17
--- /dev/null
+++ b/owl_demo/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from . import controllers
diff --git a/owl_demo/__manifest__.py b/owl_demo/__manifest__.py
new file mode 100644
index 00000000..93565bfa
--- /dev/null
+++ b/owl_demo/__manifest__.py
@@ -0,0 +1,32 @@
+{
+ "name": "OWL开发演示",
+ "summary": "示例OWL开发模块。",
+ "description": "用于演示OWL模块的开发。",
+ "author": "Van",
+ "website": "https://topodoo.com",
+ "category": "Tutorials",
+ "version": "15.0.0.1",
+ "depends": ["sale", "sale_management"],
+ "demo": [],
+ "data": [
+ #'report/test_sale_report.xml',
+ 'views/views.xml',
+ ],
+ 'assets': {
+ 'web.assets_qweb': [
+ # 'owl_demo/static/src/xml/MyComponent.xml',
+
+ #'owl_demo/static/src/js/html_template/template_demo_field.xml',
+ 'owl_demo/static/src/js/3d_viewer/3d_viewer.xml',
+ ],
+ 'web.assets_backend': [
+ # 'owl_demo/static/src/xml/MyComponent.xml',
+ # 'owl_demo/static/src/js/components/MyComponent.js',
+ # 'owl_demo/static/src/js/services/*',
+
+ #'owl_demo/static/src/js/html_template/*',
+ 'owl_demo/static/src/js/3d_viewer/*',
+ 'owl_demo/static/src/lib/model-viewer.min.js',
+ ],
+ }
+}
\ No newline at end of file
diff --git a/owl_demo/controllers/__init__.py b/owl_demo/controllers/__init__.py
new file mode 100644
index 00000000..deec4a8b
--- /dev/null
+++ b/owl_demo/controllers/__init__.py
@@ -0,0 +1 @@
+from . import main
\ No newline at end of file
diff --git a/owl_demo/controllers/main.py b/owl_demo/controllers/main.py
new file mode 100644
index 00000000..5ecd7bc3
--- /dev/null
+++ b/owl_demo/controllers/main.py
@@ -0,0 +1,266 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import base64
+import io
+import json
+import os
+
+from odoo import _
+from odoo import http
+from odoo.http import request
+
+
+class Viewer3d(http.Controller):
+
+ @http.route(['/Viewer3d/'], type='http', auth="None")
+ def viewer3d(self, attachment_id, **kwargs):
+ attachment = self._attachment_get(attachment_id)
+ return attachment
+
+ def _attachment_get(self, attachment_id):
+ attachment = request.env['ir.attachment'].sudo().browse(attachment_id)
+ if not attachment:
+ return
+ return attachment.datas
+
+ def stl_to_gltf(binary_stl_path, out_path, is_binary):
+ import struct
+
+ gltf2 = '''
+ {
+ "scenes" : [
+ {
+ "nodes" : [ 0 ]
+ }
+ ],
+ "nodes" : [
+ {
+ "mesh" : 0
+ }
+ ],
+ "meshes" : [
+ {
+ "primitives" : [ {
+ "attributes" : {
+ "POSITION" : 1
+ },
+ "indices" : 0
+ } ]
+ }
+ ],
+ "buffers" : [
+ {
+ %s
+ "byteLength" : %d
+ }
+ ],
+ "bufferViews" : [
+ {
+ "buffer" : 0,
+ "byteOffset" : 0,
+ "byteLength" : %d,
+ "target" : 34963
+ },
+ {
+ "buffer" : 0,
+ "byteOffset" : %d,
+ "byteLength" : %d,
+ "target" : 34962
+ }
+ ],
+ "accessors" : [
+ {
+ "bufferView" : 0,
+ "byteOffset" : 0,
+ "componentType" : 5125,
+ "count" : %d,
+ "type" : "SCALAR",
+ "max" : [ %d ],
+ "min" : [ 0 ]
+ },
+ {
+ "bufferView" : 1,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : %d,
+ "type" : "VEC3",
+ "min" : [%f, %f, %f],
+ "max" : [%f, %f, %f]
+ }
+ ],
+ "asset" : {
+ "version" : "2.0"
+ }
+ }
+ '''
+
+ header_bytes = 80
+ unsigned_long_int_bytes = 4
+ float_bytes = 4
+ vec3_bytes = 4 * 3
+ spacer_bytes = 2
+ num_vertices_in_face = 3
+
+ vertices = {}
+ indices = []
+
+ if not is_binary:
+ out_bin = os.path.join(out_path, "out.bin")
+ out_gltf = os.path.join(out_path, "out.gltf")
+ else:
+ out_bin = out_path
+
+ unpack_face = struct.Struct("<12fH").unpack
+ face_bytes = float_bytes * 12 + 2
+
+ with open(path_to_stl, "rb") as f:
+ f.seek(header_bytes) # skip 80 bytes headers
+
+ num_faces_bytes = f.read(unsigned_long_int_bytes)
+ number_faces = struct.unpack(" maxx: maxx = x
+ if y < miny: miny = y
+ if y > maxy: maxy = y
+ if z < minz: minz = z
+ if z > maxz: maxz = z
+
+ # f.seek(spacer_bytes, 1) # skip the spacer
+
+ number_vertices = len(vertices)
+ vertices_bytelength = number_vertices * vec3_bytes # each vec3 has 3 floats, each float is 4 bytes
+ unpadded_indices_bytelength = number_vertices * unsigned_long_int_bytes
+
+ out_number_vertices = len(vertices)
+ out_number_indices = len(indices)
+
+ unpadded_indices_bytelength = out_number_indices * unsigned_long_int_bytes
+ indices_bytelength = (unpadded_indices_bytelength + 3) & ~3
+
+ out_bin_bytelength = vertices_bytelength + indices_bytelength
+
+ if is_binary:
+ out_bin_uir = ""
+ else:
+ out_bin_uir = '"uri": "out.bin",'
+
+ gltf2 = gltf2 % (out_bin_uir,
+ # buffer
+ out_bin_bytelength,
+
+ # bufferViews[0]
+ indices_bytelength,
+
+ # bufferViews[1]
+ indices_bytelength,
+ vertices_bytelength,
+
+ # accessors[0]
+ out_number_indices,
+ out_number_vertices - 1,
+
+ # accessors[1]
+ out_number_vertices,
+ minx, miny, minz,
+ maxx, maxy, maxz
+ )
+
+ glb_out = bytearray()
+ if is_binary:
+ gltf2 = gltf2.replace(" ", "")
+ gltf2 = gltf2.replace("\n", "")
+
+ scene = bytearray(gltf2.encode())
+
+ scene_len = len(scene)
+ padded_scene_len = (scene_len + 3) & ~3
+ body_offset = padded_scene_len + 12 + 8
+
+ file_len = body_offset + out_bin_bytelength + 8
+
+ # 12-byte header
+ glb_out.extend(struct.pack('
+
+
+
+ hello jacker!
+
+
+
\ No newline at end of file
diff --git a/owl_demo/static/src/css/MyWidget.css b/owl_demo/static/src/css/MyWidget.css
new file mode 100644
index 00000000..12e077c3
--- /dev/null
+++ b/owl_demo/static/src/css/MyWidget.css
@@ -0,0 +1,30 @@
+.o_int_colorpicker {
+ .o_color_pill {
+ display: inline-block;
+ height: 25px;
+ width: 25px;
+ margin: 4px;
+ border-radius: 25px;
+ position: relative;
+ @for $size from 1 through length($o-colors) {
+ &.o_color_#{$size - 1} {
+ background-color: nth($o-colors, $size);
+ &:not(.readonly):hover {
+ transform: scale(1.2);
+ transition: 0.3s;
+ cursor: pointer;
+ }
+ &.active:after{
+ content: "\f00c";
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ color: #fff;
+ position: absolute;
+ padding: 4px;
+ font-size: 16px;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/owl_demo/static/src/js/3d_viewer/3d_viewer.js b/owl_demo/static/src/js/3d_viewer/3d_viewer.js
new file mode 100644
index 00000000..08e76570
--- /dev/null
+++ b/owl_demo/static/src/js/3d_viewer/3d_viewer.js
@@ -0,0 +1,127 @@
+/** @odoo-module **/
+
+import { registry } from "@web/core/registry";
+import { _lt } from "@web/core/l10n/translation";
+import { standardFieldProps } from "@web/views/fields/standard_field_props";
+import { useInputField } from "@web/views/fields/input_field_hook";
+import { FileUploader } from "@web/views/fields/file_handler";
+import { session } from "@web/session";
+import { useService } from "@web/core/utils/hooks";
+import { isBinarySize } from "@web/core/utils/binary";
+import { download } from "@web/core/network/download";
+import utils from 'web.utils';
+
+import core from 'web.core';
+import rpc from 'web.rpc';
+
+var QWeb = core.qweb;
+
+import { Component, onWillUpdateProps, useState, useRef, useEffect } from "@odoo/owl";
+
+export class StepViewer extends Component {
+ setup() {
+ this.notification = useService("notification");
+ this.state = useState({
+ fileName: this.props.record.data[this.props.fileNameField] || "",
+ });
+ onWillUpdateProps((nextProps) => {
+ if (nextProps.readonly) {
+ this.state.fileName = nextProps.record.data[nextProps.fileNameField] || "";
+ }
+ });
+
+ this.show_template = useRef('showStepViewer')
+ useInputField({
+ getValue: () => this.props.value || ""
+ });
+
+ useEffect(
+ (el) => {
+ if (!el) {
+ return;
+ }
+ return this.formatTemplate($(el));
+ },
+ () => [this.show_template.el],
+ )
+ }
+
+ formatTemplate($el) {
+ console.log($el)
+ var self=this;
+ if (this.props.readonly && this.props.value) {
+ var url = "/owl_demo/static/src/js/3d_viewer/test.glb";
+// var session = this.getSession();
+// if (this.props.value) {
+// if (utils.is_bin_size(this.props.value)) {
+// url = session.url("/web/content", {
+// model: this.model,
+// id: JSON.stringify(this.props.record.res_id),
+// field: this.props.record.name,
+// });
+// } else {
+// url = "data:model/gltf-binary;base64," + this.propsvalue.value;
+// }
+// }
+ return $el.html(QWeb.render("owl_demo.StepViewer",{'url':url}))
+
+ }
+ }
+
+ get fileName() {
+ return this.state.fileName || this.props.value || "";
+ }
+
+
+ update({ data, name }) {
+ this.state.fileName = name || "";
+ const { fileNameField, record } = this.props;
+ const changes = { [this.props.name]: data || false };
+ if (fileNameField in record.fields && record.data[fileNameField] !== name) {
+ changes[fileNameField] = name || false;
+ }
+ return this.props.record.update(changes);
+ }
+
+ async onFileDownload() {
+ await download({
+ data: {
+ model: this.props.record.resModel,
+ id: this.props.record.resId,
+ field: this.props.name,
+ filename_field: this.fileName,
+ filename: this.fileName || "",
+ download: true,
+ data: isBinarySize(this.props.value) ? null : this.props.value,
+ },
+ url: "/web/content",
+ });
+ }
+
+}
+
+StepViewer.template = "owl_demo.BinaryField3d";
+
+StepViewer.displayName = _lt("3D File");
+StepViewer.supportedTypes = ["binary"];
+
+StepViewer.components = {
+ FileUploader,
+};
+StepViewer.props = {
+ ...standardFieldProps,
+ acceptedFileExtensions: { type: String, optional: true },
+ fileNameField: { type: String, optional: true },
+};
+StepViewer.defaultProps = {
+ acceptedFileExtensions: "*.stp",
+};
+
+StepViewer.extractProps = ({ attrs }) => {
+ return {
+ acceptedFileExtensions: attrs.options.accepted_file_extensions,
+ fileNameField: attrs.filename,
+ };
+};
+
+registry.category("fields").add("Viewer3D", StepViewer);
diff --git a/owl_demo/static/src/js/3d_viewer/3d_viewer.xml b/owl_demo/static/src/js/3d_viewer/3d_viewer.xml
new file mode 100644
index 00000000..dd277106
--- /dev/null
+++ b/owl_demo/static/src/js/3d_viewer/3d_viewer.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/owl_demo/static/src/js/3d_viewer/stl2gltf.py b/owl_demo/static/src/js/3d_viewer/stl2gltf.py
new file mode 100644
index 00000000..1bdcc942
--- /dev/null
+++ b/owl_demo/static/src/js/3d_viewer/stl2gltf.py
@@ -0,0 +1,277 @@
+import os
+
+def stl_to_gltf(binary_stl_path, out_path, is_binary):
+ import struct
+
+ gltf2 = '''
+{
+ "scenes" : [
+ {
+ "nodes" : [ 0 ]
+ }
+ ],
+ "nodes" : [
+ {
+ "mesh" : 0
+ }
+ ],
+ "meshes" : [
+ {
+ "primitives" : [ {
+ "attributes" : {
+ "POSITION" : 1
+ },
+ "indices" : 0
+ } ]
+ }
+ ],
+ "buffers" : [
+ {
+ %s
+ "byteLength" : %d
+ }
+ ],
+ "bufferViews" : [
+ {
+ "buffer" : 0,
+ "byteOffset" : 0,
+ "byteLength" : %d,
+ "target" : 34963
+ },
+ {
+ "buffer" : 0,
+ "byteOffset" : %d,
+ "byteLength" : %d,
+ "target" : 34962
+ }
+ ],
+ "accessors" : [
+ {
+ "bufferView" : 0,
+ "byteOffset" : 0,
+ "componentType" : 5125,
+ "count" : %d,
+ "type" : "SCALAR",
+ "max" : [ %d ],
+ "min" : [ 0 ]
+ },
+ {
+ "bufferView" : 1,
+ "byteOffset" : 0,
+ "componentType" : 5126,
+ "count" : %d,
+ "type" : "VEC3",
+ "min" : [%f, %f, %f],
+ "max" : [%f, %f, %f]
+ }
+ ],
+ "asset" : {
+ "version" : "2.0"
+ }
+}
+'''
+
+ header_bytes = 80
+ unsigned_long_int_bytes = 4
+ float_bytes = 4
+ vec3_bytes = 4 * 3
+ spacer_bytes = 2
+ num_vertices_in_face = 3
+
+ vertices = {}
+ indices = []
+
+ if not is_binary:
+ out_bin = os.path.join(out_path, "out.bin")
+ out_gltf = os.path.join(out_path, "out.gltf")
+ else:
+ out_bin = out_path
+
+ unpack_face = struct.Struct("<12fH").unpack
+ face_bytes = float_bytes*12 + 2
+
+ with open(path_to_stl, "rb") as f:
+ f.seek(header_bytes) # skip 80 bytes headers
+
+ num_faces_bytes = f.read(unsigned_long_int_bytes)
+ number_faces = struct.unpack(" maxx: maxx = x
+ if y < miny: miny = y
+ if y > maxy: maxy = y
+ if z < minz: minz = z
+ if z > maxz: maxz = z
+
+ # f.seek(spacer_bytes, 1) # skip the spacer
+
+ number_vertices = len(vertices)
+ vertices_bytelength = number_vertices * vec3_bytes # each vec3 has 3 floats, each float is 4 bytes
+ unpadded_indices_bytelength = number_vertices * unsigned_long_int_bytes
+
+ out_number_vertices = len(vertices)
+ out_number_indices = len(indices)
+
+ unpadded_indices_bytelength = out_number_indices * unsigned_long_int_bytes
+ indices_bytelength = (unpadded_indices_bytelength + 3) & ~3
+
+ out_bin_bytelength = vertices_bytelength + indices_bytelength
+
+ if is_binary:
+ out_bin_uir = ""
+ else:
+ out_bin_uir = '"uri": "out.bin",'
+
+ gltf2 = gltf2 % ( out_bin_uir,
+ #buffer
+ out_bin_bytelength,
+
+ # bufferViews[0]
+ indices_bytelength,
+
+ # bufferViews[1]
+ indices_bytelength,
+ vertices_bytelength,
+
+ # accessors[0]
+ out_number_indices,
+ out_number_vertices - 1,
+
+ # accessors[1]
+ out_number_vertices,
+ minx, miny, minz,
+ maxx, maxy, maxz
+ )
+
+ glb_out = bytearray()
+ if is_binary:
+ gltf2 = gltf2.replace(" ", "")
+ gltf2 = gltf2.replace("\n", "")
+
+ scene = bytearray(gltf2.encode())
+
+ scene_len = len(scene)
+ padded_scene_len = (scene_len + 3) & ~3
+ body_offset = padded_scene_len + 12 + 8
+
+ file_len = body_offset + out_bin_bytelength + 8
+
+ # 12-byte header
+ glb_out.extend(struct.pack(' 3:
+ is_binary = True
+ else:
+ is_binary = False
+
+ if out_path.lower().endswith(".glb"):
+ print("Use binary mode since output file has glb extension")
+ is_binary = True
+ else:
+ if is_binary:
+ print("output file should have glb extension but not %s", out_path)
+
+ if not os.path.exists(path_to_stl):
+ print("stl file does not exists %s" % path_to_stl)
+
+ if not is_binary:
+ if not os.path.isdir(out_path):
+ os.mkdir(out_path)
+
+ stl_to_gltf(path_to_stl, out_path, is_binary)
\ No newline at end of file
diff --git a/owl_demo/static/src/js/3d_viewer/test.glb b/owl_demo/static/src/js/3d_viewer/test.glb
new file mode 100644
index 00000000..c4a6352b
Binary files /dev/null and b/owl_demo/static/src/js/3d_viewer/test.glb differ
diff --git a/owl_demo/static/src/js/components/MyComponent.js b/owl_demo/static/src/js/components/MyComponent.js
new file mode 100644
index 00000000..7000aa9e
--- /dev/null
+++ b/owl_demo/static/src/js/components/MyComponent.js
@@ -0,0 +1,25 @@
+/** @odoo-module **/
+
+const { Component, useState, mount, whenReady, xml } = owl;
+
+export class MyComponent extends Component {
+ static template = 'owl_demo.my_component'
+
+
+ setup() {
+ this.state = useState({ is_show:true});
+ }
+
+ onRemove(ev) {
+ this.state.is_show = false;
+ }
+
+}
+
+//MyComponent.template = 'owl_demo.MyComponent';
+
+whenReady(() => {
+ var my_component = new MyComponent();
+ mount(my_component, document.body);
+ //$("#myModal").modal('show');
+});
\ No newline at end of file
diff --git a/owl_demo/static/src/js/components/PartnerOrderSummary.js b/owl_demo/static/src/js/components/PartnerOrderSummary.js
new file mode 100644
index 00000000..39cce4ba
--- /dev/null
+++ b/owl_demo/static/src/js/components/PartnerOrderSummary.js
@@ -0,0 +1,84 @@
+/** @odoo-module **/
+
+const { Component, useState, mount, xml } = owl;
+
+import { registry } from "@web/core/registry";
+import { formView } from "@web/views/form/form_view";
+import { FormCompiler } from "@web/views/form/form_compiler";
+import { FormRenderer } from '@web/views/form/form_renderer';
+
+class PartnerOrderSummary extends Component {
+ partner = useState({});
+ constructor(self, partner) {
+ super();
+ this.partner = partner;
+ }
+};
+
+PartnerOrderSummary.template = "owl_demo.PartnerOrderSummary";
+
+
+function compilePartner(el, params) {
+// this._rpc({
+// model: "res.partner",
+// method: "read",
+// args: [[this.state.data.partner_id.res_id]]
+// }).then(data => {
+// (new ComponentWrapper(this,
+// PartnerOrderSummary,
+// useState(data[0])
+// )).mount(element);
+// });
+
+ return "hello world
";
+}
+
+function compileForm() {
+ const res = this.compileForm(...arguments);
+ res.classList.append("test111");
+ return res;
+}
+
+export class SaleOrderFormCompiler extends FormCompiler {
+ setup() {
+ super.setup();
+ this.compilers.unshift(
+ { selector: "form", fn: compileForm },
+ { selector: "div .o_partner_order_summary", fn: compilePartner }
+ );
+ }
+}
+
+export class SaleOrderFormRenderer extends FormRenderer { }
+
+SaleOrderFormRenderer.components = {
+ ...FormRenderer.components,
+ PartnerOrderSummary,
+};
+
+registry.category('views').add('sale.view_order_form', {
+ ...formView,
+ Compiler: SaleOrderFormCompiler,
+ Renderer: SaleOrderFormRenderer,
+});
+
+
+/*** 重载表单渲染器,对任何包含o_partner_order_summary这一class 的div挂载当前组件 */
+//FormRenderer.include({
+// _render: async function() {
+// await this._super(...arguments);
+// for (const element of this.el.querySelectorAll(".o_partner_order_summary")) {
+// this._rpc({
+// model: "res.partner",
+// method: "read",
+// args: [[this.state.data.partner_id.res_id]]
+// }).then(data => {
+// (new ComponentWrapper(this,
+// PartnerOrderSummary,
+// useState(data[0])
+// )).mount(element);
+// });
+// }
+// }
+//});
+
diff --git a/owl_demo/static/src/js/html_template/template_demo_field.xml b/owl_demo/static/src/js/html_template/template_demo_field.xml
new file mode 100644
index 00000000..43b015d1
--- /dev/null
+++ b/owl_demo/static/src/js/html_template/template_demo_field.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+ - Cras justo odio
+ - Dapibus ac facilisis in
+ - Morbi leo risus
+ - Porta ac consectetur ac
+ - Vestibulum at eros
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/owl_demo/static/src/js/html_template/template_field.js b/owl_demo/static/src/js/html_template/template_field.js
new file mode 100644
index 00000000..90b2425c
--- /dev/null
+++ b/owl_demo/static/src/js/html_template/template_field.js
@@ -0,0 +1,97 @@
+/** @odoo-module **/
+
+import { registry } from "@web/core/registry";
+import { _lt } from "@web/core/l10n/translation";
+import { standardFieldProps } from "@web/views/fields/standard_field_props";
+import { useInputField } from "@web/views/fields/input_field_hook";
+import { session } from "@web/session";
+import core from 'web.core';
+import rpc from 'web.rpc';
+
+var QWeb = core.qweb;
+
+import { Component,useRef,useEffect } from "@odoo/owl";
+
+export class CTemplateField extends Component {
+ setup() {
+ this.show_template = useRef('showTemplate')
+ useInputField({
+ getValue: () => this.props.value || ""
+ });
+
+ useEffect(
+ (el) => {
+ if (!el) {
+ return;
+ }
+ return this.formatTemplate($(el));
+ },
+ () => [this.show_template.el],
+ )
+ }
+
+ formatTemplate($el) {
+ console.log($el)
+ var self=this;
+ if (this.props.readonly && this.props.value) {
+ //value = this.props.value + ' | ' + this.props.template_xml_id;
+ if (this.props.type == 'one2many' || this.props.type == 'many2many'){
+ // 如果是one2many或many2many,就重新获取对象列表
+ rpc.query({
+ model: this.props.value.resModel,
+ method: 'read',
+ args: [this.props.value.currentIds,[]],
+ }).then((result) => {
+ console.log(result);
+ return $el.html(QWeb.render(this.props.template_xml_id,{'record':this.props.record,'current_field':result}))
+ });
+ }
+ else if (this.props.type == 'many2one'){
+ // 如果是one2many或many2many,就重新获取对象列表
+ rpc.query({
+ model: this.props.record.fields[this.props.name].relation,
+ method: 'read',
+ args: [this.props.value[0],[]],
+ }).then((result) => {
+ console.log(result);
+ return $el.html(QWeb.render(this.props.template_xml_id,{'record':this.props.record,'current_field':result[0]}))
+ });
+ }
+ else{
+ if (this.props.template_xml_id === undefined)
+ {
+ return $el.html(this.props.value);
+ }else{
+ return $el.html(QWeb.render(this.props.template_xml_id,{'record':this.props.record,'current_field':this.props.value}));
+ }
+
+ }
+ }
+
+ //
+ }
+
+}
+
+CTemplateField.template = "owl_demo.CTemplateField";
+CTemplateField.props = {
+ ...standardFieldProps,
+ template_xml_id: { type: String, optional: true },
+ placeholder: { type: String, optional: true },
+};
+CTemplateField.defaultProps = {
+// hideSymbol: false,
+// inputType: "text",
+};
+
+CTemplateField.supportedTypes = ["many2one","char"];
+CTemplateField.displayName = _lt("CTemplate");
+
+CTemplateField.extractProps = ({ attrs }) => {
+ return {
+ template_xml_id: attrs.options.template,
+ placeholder: attrs.placeholder,
+ };
+};
+
+registry.category("fields").add("CTemplate", CTemplateField);
diff --git a/owl_demo/static/src/js/html_template/template_field.scss b/owl_demo/static/src/js/html_template/template_field.scss
new file mode 100644
index 00000000..021ef387
--- /dev/null
+++ b/owl_demo/static/src/js/html_template/template_field.scss
@@ -0,0 +1,3 @@
+.o_field_widget.o_field_template {
+ display: block;
+}
diff --git a/owl_demo/static/src/js/html_template/template_field.xml b/owl_demo/static/src/js/html_template/template_field.xml
new file mode 100644
index 00000000..fbeb7274
--- /dev/null
+++ b/owl_demo/static/src/js/html_template/template_field.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/owl_demo/static/src/js/services/notification.js b/owl_demo/static/src/js/services/notification.js
new file mode 100644
index 00000000..2096cd3a
--- /dev/null
+++ b/owl_demo/static/src/js/services/notification.js
@@ -0,0 +1,25 @@
+/** @odoo-module **/
+
+import { registry } from "@web/core/registry";
+
+const serviceRegistry = registry.category("services");
+
+const myService = {
+ dependencies: ["notification","title","effect"],
+ start(env, { notification, title,effect }) {
+
+// let counter = 1;
+// setInterval(() => {
+// var tik_str = `Tick Tock ${counter++}`;
+// notification.add(tik_str);
+// title.setParts({ odoo: tik_str, fruit: tik_str });
+// effect.add({
+// type: "rainbow_man", // can be omitted, default type is already "rainbow_man"
+// message: "Boom! Team record for the past 30 days." + tik_str,
+// });
+// }, 5000);
+
+ }
+};
+
+serviceRegistry.add("myService", myService);
\ No newline at end of file
diff --git a/owl_demo/static/src/js/widgets/MyWidget.js b/owl_demo/static/src/js/widgets/MyWidget.js
new file mode 100644
index 00000000..49452cc6
--- /dev/null
+++ b/owl_demo/static/src/js/widgets/MyWidget.js
@@ -0,0 +1,50 @@
+/** @odoo-module **/
+
+import AbstractField from 'web.AbstractField';
+import fieldRegistry from 'web.field_registry';
+
+export const ShowUnitsWidgetField = AbstractField.extend({
+ supportedFieldTypes: ['float','char','datetime'],
+
+ /**
+ * @override
+ */
+// init: function () {
+// this._super.apply(this, arguments);
+// this.units = this.nodeOptions && this.nodeOptions.units || '';
+// },
+
+ /**
+ * @override
+ */
+ _renderReadonly() {
+ this.units = this.nodeOptions && this.nodeOptions.units || '';
+ this.$el.empty().html(this._formatValue(this.value) + " "+this.units+"");
+ },
+
+});
+
+fieldRegistry.add('show_units', ShowUnitsWidgetField);
+
+export const ShowUnitsWidgetField = AbstractField.extend({
+ supportedFieldTypes: ['float','char','datetime'],
+
+ /**
+ * @override
+ */
+// init: function () {
+// this._super.apply(this, arguments);
+// this.units = this.nodeOptions && this.nodeOptions.units || '';
+// },
+
+ /**
+ * @override
+ */
+ _renderReadonly() {
+ this.units = this.nodeOptions && this.nodeOptions.units || '';
+ this.$el.empty().html(this._formatValue(this.value) + " "+this.units+"");
+ },
+
+});
+
+fieldRegistry.add('show_units', ShowUnitsWidgetField);
\ No newline at end of file
diff --git a/owl_demo/static/src/xml/MyComponent.xml b/owl_demo/static/src/xml/MyComponent.xml
new file mode 100644
index 00000000..6db9e2b2
--- /dev/null
+++ b/owl_demo/static/src/xml/MyComponent.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/owl_demo/static/src/xml/MyWidget.xml b/owl_demo/static/src/xml/MyWidget.xml
new file mode 100644
index 00000000..00a6b92c
--- /dev/null
+++ b/owl_demo/static/src/xml/MyWidget.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/owl_demo/static/src/xml/PartnerOrderSummary.xml b/owl_demo/static/src/xml/PartnerOrderSummary.xml
new file mode 100644
index 00000000..1f1c5664
--- /dev/null
+++ b/owl_demo/static/src/xml/PartnerOrderSummary.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/owl_demo/views/views.xml b/owl_demo/views/views.xml
new file mode 100644
index 00000000..b558e85b
--- /dev/null
+++ b/owl_demo/views/views.xml
@@ -0,0 +1,33 @@
+
+
+
+ sale.order.form.inherit
+ sale.order
+
+
+
+
+
+
+
+
+
+
+
+
+ sale.order.tree.inherit
+ sale.order
+
+
+
+ CTemplate
+ {'template':'owl_demo.field_partner_id'}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_bf_connect/controllers/controllers.py b/sf_bf_connect/controllers/controllers.py
index 62a19f64..d84f1e07 100644
--- a/sf_bf_connect/controllers/controllers.py
+++ b/sf_bf_connect/controllers/controllers.py
@@ -105,6 +105,7 @@ class Sf_Bf_Connect(http.Controller):
product_bom_purchase.with_user(request.env.ref("base.user_admin")).bom_create_line_has(
purchase_embryo)
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
+ order_id.step_file = product.model_file
i += 1
res['factory_order_no'] = order_id.name
return json.JSONEncoder().encode(res)
diff --git a/sf_manufacturing/models/mrp_routing_workcenter.py b/sf_manufacturing/models/mrp_routing_workcenter.py
index 441fddf3..dde6eff6 100644
--- a/sf_manufacturing/models/mrp_routing_workcenter.py
+++ b/sf_manufacturing/models/mrp_routing_workcenter.py
@@ -24,16 +24,29 @@ class ResMrpRoutingWorkcenter(models.Model):
company_id = fields.Many2one('res.company', compute="get_company_id", related=False)
+ # 排产的时候, 根据胚料的长宽高比对一下机床的最大加工尺寸.不符合就不要分配给这个加工中心(机床).
# 工单对应的工作中心,根据工序中的工作中心去匹配,
# 如果只配置了一个工作中心,则默认采用该工作中心;
# 如果有多个工作中心,
# 则根据该工作中心的工单个数进行分配(优先分配给工单个数最少的);
- def get_workcenter(self, workcenter_ids):
+ def get_workcenter(self, workcenter_ids, routing_type, product):
if workcenter_ids:
if len(workcenter_ids) == 1:
return workcenter_ids[0]
elif len(workcenter_ids) >= 2:
# workcenter_ids_str = ','.join([str(s) for s in workcenter_ids])
+ if routing_type == 'CNC加工':
+ i = 0
+ # for item in workcenter_ids:
+ #
+ # machine_tool = self.env['sf.machine_tool'].search(
+ # [('x_axis', '>', product.length), ('y_axis', '>', product.width),
+ # ('z_axis', '>', product.height)])
+ # if machine_tool:
+ # for item in machine_tool:
+ # workcenter_ids = item.
+
+
self.env.cr.execute("""
SELECT workcenter_id FROM mrp_workorder where workcenter_id
in %s group by workcenter_id
diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py
index e5f6d408..4ba989d1 100644
--- a/sf_manufacturing/models/mrp_workorder.py
+++ b/sf_manufacturing/models/mrp_workorder.py
@@ -145,7 +145,7 @@ class ResMrpWorkOrder(models.Model):
'processing_panel': k,
'routing_type': route.routing_type,
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
- 'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids),
+ 'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,route.routing_type,production.product_id),
'date_planned_start': False,
'date_planned_finished': False,
'duration_expected': 60,