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(' + + + \ 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 @@ + + + +
+ + +

+ +

+ + + +

+ +
+
+

+ +

+

Orders

+
+
+

+ +

+

Total Sales

+
+
+
+
+
\ 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,