Accept Merge Request #141: (feature/页面优化 -> develop)
Merge Request: 页面优化及排产(根据胚料的长宽高比对一下机床的最大加工尺寸) Created By: @杨金灵 Accepted By: @杨金灵 URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/141?initial=true
This commit is contained in:
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# 默认忽略的文件
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# 基于编辑器的 HTTP 客户端请求
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
6
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
20
.idea/jikimo-sf.iml
generated
20
.idea/jikimo-sf.iml
generated
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
<orderEntry type="module" module-name="odoo-16.0" />
|
|
||||||
</component>
|
|
||||||
<component name="PyDocumentationSettings">
|
|
||||||
<option name="format" value="PLAIN" />
|
|
||||||
<option name="myDocStringFormat" value="Plain" />
|
|
||||||
</component>
|
|
||||||
<component name="TemplatesService">
|
|
||||||
<option name="TEMPLATE_FOLDERS">
|
|
||||||
<list>
|
|
||||||
<option value="$MODULE_DIR$/yizuo_login_background_and_styles/templates" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
4
.idea/misc.xml
generated
4
.idea/misc.xml
generated
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (odoo-15.0)" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
9
.idea/modules.xml
generated
9
.idea/modules.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/jikimo-sf.iml" filepath="$PROJECT_DIR$/.idea/jikimo-sf.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/../../../odoo-16.0/.idea/odoo-16.0.iml" filepath="$PROJECT_DIR$/../../../odoo-16.0/.idea/odoo-16.0.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
2
owl_demo/__init__.py
Normal file
2
owl_demo/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import models
|
||||||
|
from . import controllers
|
||||||
32
owl_demo/__manifest__.py
Normal file
32
owl_demo/__manifest__.py
Normal file
@@ -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',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
1
owl_demo/controllers/__init__.py
Normal file
1
owl_demo/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import main
|
||||||
266
owl_demo/controllers/main.py
Normal file
266
owl_demo/controllers/main.py
Normal file
@@ -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/<int:attachment_id>'], 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("<I", num_faces_bytes)[0]
|
||||||
|
|
||||||
|
# the vec3_bytes is for normal
|
||||||
|
stl_assume_bytes = header_bytes + unsigned_long_int_bytes + number_faces * (
|
||||||
|
vec3_bytes * 3 + spacer_bytes + vec3_bytes)
|
||||||
|
assert stl_assume_bytes == os.path.getsize(path_to_stl), "stl is not binary or ill formatted"
|
||||||
|
|
||||||
|
minx, maxx = [9999999, -9999999]
|
||||||
|
miny, maxy = [9999999, -9999999]
|
||||||
|
minz, maxz = [9999999, -9999999]
|
||||||
|
|
||||||
|
vertices_length_counter = 0
|
||||||
|
|
||||||
|
data = struct.unpack("<" + "12fH" * number_faces, f.read())
|
||||||
|
len_data = len(data)
|
||||||
|
|
||||||
|
for i in range(0, len_data, 13):
|
||||||
|
for j in range(3, 12, 3):
|
||||||
|
x, y, z = data[i + j:i + j + 3]
|
||||||
|
|
||||||
|
x = int(x * 100000) / 100000
|
||||||
|
y = int(y * 100000) / 100000
|
||||||
|
z = int(z * 100000) / 100000
|
||||||
|
|
||||||
|
tuple_xyz = (x, y, z);
|
||||||
|
|
||||||
|
try:
|
||||||
|
indices.append(vertices[tuple_xyz])
|
||||||
|
except KeyError:
|
||||||
|
vertices[tuple_xyz] = vertices_length_counter
|
||||||
|
vertices_length_counter += 1
|
||||||
|
indices.append(vertices[tuple_xyz])
|
||||||
|
|
||||||
|
if x < minx: minx = x
|
||||||
|
if x > 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('<I', 0x46546C67)) # magic number for glTF
|
||||||
|
glb_out.extend(struct.pack('<I', 2))
|
||||||
|
glb_out.extend(struct.pack('<I', file_len))
|
||||||
|
|
||||||
|
# chunk 0
|
||||||
|
glb_out.extend(struct.pack('<I', padded_scene_len))
|
||||||
|
glb_out.extend(struct.pack('<I', 0x4E4F534A)) # magic number for JSON
|
||||||
|
glb_out.extend(scene)
|
||||||
|
|
||||||
|
while len(glb_out) < body_offset:
|
||||||
|
glb_out.extend(b' ')
|
||||||
|
|
||||||
|
# chunk 1
|
||||||
|
glb_out.extend(struct.pack('<I', out_bin_bytelength))
|
||||||
|
glb_out.extend(struct.pack('<I', 0x004E4942)) # magin number for BIN
|
||||||
|
|
||||||
|
# print('<%dI' % len(indices))
|
||||||
|
# print(struct.pack('<%dI' % len(indices), *indices))
|
||||||
|
glb_out.extend(struct.pack('<%dI' % len(indices), *indices))
|
||||||
|
|
||||||
|
for i in range(indices_bytelength - unpadded_indices_bytelength):
|
||||||
|
glb_out.extend(b' ')
|
||||||
|
|
||||||
|
vertices = dict((v, k) for k, v in vertices.items())
|
||||||
|
|
||||||
|
# glb_out.extend(struct.pack('f',
|
||||||
|
# print([each_v for vertices[v_counter] for v_counter in range(number_vertices)]) # magin number for BIN
|
||||||
|
vertices = [vertices[i] for i in range(number_vertices)]
|
||||||
|
flatten = lambda l: [item for sublist in l for item in sublist]
|
||||||
|
|
||||||
|
# for v_counter in :
|
||||||
|
# v_3f = vertices[v_counter]
|
||||||
|
# all_floats_in_vertices.append(v_3f[0])
|
||||||
|
# all_floats_in_vertices.append(v_3f[1])
|
||||||
|
# all_floats_in_vertices.append(v_3f[2])
|
||||||
|
|
||||||
|
# for v_counter in range(number_vertices):
|
||||||
|
glb_out.extend(struct.pack('%df' % number_vertices * 3, *flatten(vertices))) # magin number for BIN
|
||||||
|
|
||||||
|
# for v_counter in range(number_vertices):
|
||||||
|
# glb_out.extend(struct.pack('3f', *vertices[v_counter])) # magin number for BIN
|
||||||
|
|
||||||
|
# for (v_x, v_y, v_z), _ in sorted(vertices.items(), key=lambda x: x[1]):
|
||||||
|
# glb_out.extend(struct.pack('3f', v_x, v_y, v_z)) # magin number for BIN
|
||||||
|
# # glb_out.extend(struct.pack('f', v_y)) # magin number for BIN
|
||||||
|
# # glb_out.extend(struct.pack('f', v_z)) # magin number for BIN
|
||||||
|
|
||||||
|
with open(out_bin, "wb") as out:
|
||||||
|
out.write(glb_out)
|
||||||
|
|
||||||
|
if not is_binary:
|
||||||
|
with open(out_gltf, "w") as out:
|
||||||
|
out.write(gltf2)
|
||||||
1
owl_demo/models/__init__.py
Normal file
1
owl_demo/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import sale_order
|
||||||
15
owl_demo/models/sale_order.py
Normal file
15
owl_demo/models/sale_order.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import models,fields
|
||||||
|
from odoo.tools import populate, groupby
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrder(models.Model):
|
||||||
|
_inherit = "sale.order"
|
||||||
|
|
||||||
|
step_file = fields.Binary("Step File")
|
||||||
0
owl_demo/report/__init__.py
Normal file
0
owl_demo/report/__init__.py
Normal file
8
owl_demo/report/test_sale_report.xml
Normal file
8
owl_demo/report/test_sale_report.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="sale.report_saleorder_document" inherit_id="sale.report_saleorder_document">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<h1>hello jacker!</h1>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
30
owl_demo/static/src/css/MyWidget.css
Normal file
30
owl_demo/static/src/css/MyWidget.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
127
owl_demo/static/src/js/3d_viewer/3d_viewer.js
Normal file
127
owl_demo/static/src/js/3d_viewer/3d_viewer.js
Normal file
@@ -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);
|
||||||
88
owl_demo/static/src/js/3d_viewer/3d_viewer.xml
Normal file
88
owl_demo/static/src/js/3d_viewer/3d_viewer.xml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="owl_demo.BinaryField3d" owl="1">
|
||||||
|
<t t-if="!props.readonly">
|
||||||
|
<t t-if="props.value">
|
||||||
|
<div class="w-100 d-inline-flex">
|
||||||
|
<FileUploader
|
||||||
|
acceptedFileExtensions="props.acceptedFileExtensions"
|
||||||
|
file="{ data: props.value, name: fileName }"
|
||||||
|
onUploaded.bind="update"
|
||||||
|
>
|
||||||
|
<t if="props.record.resId">
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary fa fa-download"
|
||||||
|
data-tooltip="Download"
|
||||||
|
aria-label="Download"
|
||||||
|
t-on-click="onFileDownload"
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
<t t-set-slot="toggler">
|
||||||
|
<input type="text" class="o_input" t-att-value="fileName" readonly="readonly" />
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary fa fa-pencil o_select_file_button"
|
||||||
|
data-tooltip="Edit"
|
||||||
|
aria-label="Edit"
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary fa fa-trash o_clear_file_button"
|
||||||
|
data-tooltip="Clear"
|
||||||
|
aria-label="Clear"
|
||||||
|
t-on-click="() => this.update({})"
|
||||||
|
/>
|
||||||
|
</FileUploader>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<label class="o_select_file_button btn btn-primary">
|
||||||
|
<FileUploader
|
||||||
|
acceptedFileExtensions="props.acceptedFileExtensions"
|
||||||
|
onUploaded.bind="update"
|
||||||
|
>
|
||||||
|
<t t-set-slot="toggler">
|
||||||
|
Upload your file
|
||||||
|
</t>
|
||||||
|
</FileUploader>
|
||||||
|
</label>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
<t t-elif="props.record.resId and props.value">
|
||||||
|
<!-- <div t-ref="showStepViewer"/>-->
|
||||||
|
<model-viewer urc="/owl_demo/static/src/js/3d_viewer/test.glb" ar-modes="scene-viewer webxr quick-look" poster="poster.webp" shadow-intensity="1" camera-orbit="180deg 72.29deg 12640m" field-of-view="30deg">
|
||||||
|
<div class="progress-bar hide" slot="progress-bar">
|
||||||
|
<div class="update-bar"></div>
|
||||||
|
</div>
|
||||||
|
</model-viewer>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="owl_demo.StepViewer">
|
||||||
|
<model-viewer
|
||||||
|
t-att-src='url'
|
||||||
|
name="3D model"
|
||||||
|
alt="3D model"
|
||||||
|
auto-rotate="1"
|
||||||
|
camera-controls="1"
|
||||||
|
>
|
||||||
|
<div class="text-center mt-4 mb-4 mr-4">
|
||||||
|
<span
|
||||||
|
id="model-viewer-fullscreen"
|
||||||
|
title="View fullscreen"
|
||||||
|
role="img"
|
||||||
|
aria-label="Fullscreen"
|
||||||
|
>
|
||||||
|
<i class="fa fa-arrows-alt fa-2x" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- <span style="position: absolute;top: 85%;left: 0%;font-size: 9px;" t-if="widget.model == 'jikimo.process.order.line'">-->
|
||||||
|
<!-- L:<t t-esc="widget.recordData.model_length"/>,-->
|
||||||
|
<!-- W:<t t-esc="widget.recordData.model_width"/>,-->
|
||||||
|
<!-- H:<t t-esc="widget.recordData.model_height"/>,-->
|
||||||
|
<!-- V:<t t-esc="widget.recordData.model_volume"/>-->
|
||||||
|
<!-- </span>-->
|
||||||
|
</model-viewer>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
277
owl_demo/static/src/js/3d_viewer/stl2gltf.py
Normal file
277
owl_demo/static/src/js/3d_viewer/stl2gltf.py
Normal file
@@ -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("<I", num_faces_bytes)[0]
|
||||||
|
|
||||||
|
# the vec3_bytes is for normal
|
||||||
|
stl_assume_bytes = header_bytes + unsigned_long_int_bytes + number_faces * (vec3_bytes*3 + spacer_bytes + vec3_bytes)
|
||||||
|
assert stl_assume_bytes == os.path.getsize(path_to_stl), "stl is not binary or ill formatted"
|
||||||
|
|
||||||
|
minx, maxx = [9999999, -9999999]
|
||||||
|
miny, maxy = [9999999, -9999999]
|
||||||
|
minz, maxz = [9999999, -9999999]
|
||||||
|
|
||||||
|
vertices_length_counter = 0
|
||||||
|
|
||||||
|
data = struct.unpack("<" + "12fH"*number_faces, f.read())
|
||||||
|
len_data = len(data)
|
||||||
|
|
||||||
|
for i in range(0, len_data, 13):
|
||||||
|
for j in range(3, 12, 3):
|
||||||
|
x, y, z = data[i+j:i+j+3]
|
||||||
|
|
||||||
|
x = int(x*100000)/100000
|
||||||
|
y = int(y*100000)/100000
|
||||||
|
z = int(z*100000)/100000
|
||||||
|
|
||||||
|
tuple_xyz = (x, y, z);
|
||||||
|
|
||||||
|
try:
|
||||||
|
indices.append(vertices[tuple_xyz])
|
||||||
|
except KeyError:
|
||||||
|
vertices[tuple_xyz] = vertices_length_counter
|
||||||
|
vertices_length_counter += 1
|
||||||
|
indices.append(vertices[tuple_xyz])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if x < minx: minx = x
|
||||||
|
if x > 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('<I', 0x46546C67)) # magic number for glTF
|
||||||
|
glb_out.extend(struct.pack('<I', 2))
|
||||||
|
glb_out.extend(struct.pack('<I', file_len))
|
||||||
|
|
||||||
|
# chunk 0
|
||||||
|
glb_out.extend(struct.pack('<I', padded_scene_len))
|
||||||
|
glb_out.extend(struct.pack('<I', 0x4E4F534A)) # magic number for JSON
|
||||||
|
glb_out.extend(scene)
|
||||||
|
|
||||||
|
while len(glb_out) < body_offset:
|
||||||
|
glb_out.extend(b' ')
|
||||||
|
|
||||||
|
# chunk 1
|
||||||
|
glb_out.extend(struct.pack('<I', out_bin_bytelength))
|
||||||
|
glb_out.extend(struct.pack('<I', 0x004E4942)) # magin number for BIN
|
||||||
|
|
||||||
|
# print('<%dI' % len(indices))
|
||||||
|
# print(struct.pack('<%dI' % len(indices), *indices))
|
||||||
|
glb_out.extend(struct.pack('<%dI' % len(indices), *indices))
|
||||||
|
|
||||||
|
for i in range(indices_bytelength - unpadded_indices_bytelength):
|
||||||
|
glb_out.extend(b' ')
|
||||||
|
|
||||||
|
vertices = dict((v, k) for k,v in vertices.items())
|
||||||
|
|
||||||
|
# glb_out.extend(struct.pack('f',
|
||||||
|
# print([each_v for vertices[v_counter] for v_counter in range(number_vertices)]) # magin number for BIN
|
||||||
|
vertices = [vertices[i] for i in range(number_vertices)]
|
||||||
|
flatten = lambda l: [item for sublist in l for item in sublist]
|
||||||
|
|
||||||
|
# for v_counter in :
|
||||||
|
# v_3f = vertices[v_counter]
|
||||||
|
# all_floats_in_vertices.append(v_3f[0])
|
||||||
|
# all_floats_in_vertices.append(v_3f[1])
|
||||||
|
# all_floats_in_vertices.append(v_3f[2])
|
||||||
|
|
||||||
|
# for v_counter in range(number_vertices):
|
||||||
|
glb_out.extend(struct.pack('%df' % number_vertices*3, *flatten(vertices))) # magin number for BIN
|
||||||
|
|
||||||
|
# for v_counter in range(number_vertices):
|
||||||
|
# glb_out.extend(struct.pack('3f', *vertices[v_counter])) # magin number for BIN
|
||||||
|
|
||||||
|
# for (v_x, v_y, v_z), _ in sorted(vertices.items(), key=lambda x: x[1]):
|
||||||
|
# glb_out.extend(struct.pack('3f', v_x, v_y, v_z)) # magin number for BIN
|
||||||
|
# # glb_out.extend(struct.pack('f', v_y)) # magin number for BIN
|
||||||
|
# # glb_out.extend(struct.pack('f', v_z)) # magin number for BIN
|
||||||
|
|
||||||
|
with open(out_bin, "wb") as out:
|
||||||
|
out.write(glb_out)
|
||||||
|
|
||||||
|
if not is_binary:
|
||||||
|
with open(out_gltf, "w") as out:
|
||||||
|
out.write(gltf2)
|
||||||
|
|
||||||
|
print("Done! Exported to %s" %out_path)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("use it like python3 stl_to_gltf.py /path/to/stl /path/to/gltf/folder")
|
||||||
|
print("or python3 stl_to_gltf.py /path/to/stl /path/to/glb/file -b")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
path_to_stl = sys.argv[1]
|
||||||
|
out_path = sys.argv[2]
|
||||||
|
if len(sys.argv) > 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)
|
||||||
BIN
owl_demo/static/src/js/3d_viewer/test.glb
Normal file
BIN
owl_demo/static/src/js/3d_viewer/test.glb
Normal file
Binary file not shown.
25
owl_demo/static/src/js/components/MyComponent.js
Normal file
25
owl_demo/static/src/js/components/MyComponent.js
Normal file
@@ -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');
|
||||||
|
});
|
||||||
84
owl_demo/static/src/js/components/PartnerOrderSummary.js
Normal file
84
owl_demo/static/src/js/components/PartnerOrderSummary.js
Normal file
@@ -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 "<h1>hello world</h1>";
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//});
|
||||||
|
|
||||||
64
owl_demo/static/src/js/html_template/template_demo_field.xml
Normal file
64
owl_demo/static/src/js/html_template/template_demo_field.xml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="owl_demo.field_team_id">
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item active">Cras justo odio</li>
|
||||||
|
<li class="list-group-item">Dapibus ac facilisis in</li>
|
||||||
|
<li class="list-group-item">Morbi leo risus</li>
|
||||||
|
<li class="list-group-item">Porta ac consectetur ac</li>
|
||||||
|
<li class="list-group-item">Vestibulum at eros</li>
|
||||||
|
</ul>
|
||||||
|
</t>
|
||||||
|
<!-- 不要加 owl="1" -->
|
||||||
|
<t t-name="owl_demo.field_order_line">
|
||||||
|
<!-- <t t-foreach="current_field" t-as="order_line">-->
|
||||||
|
<!-- <h6><t t-out="order_line.name"/>-->
|
||||||
|
<!-- <span class="badge rounded-pill dropdown o_tag o_tag_color_7"><t t-out="order_line.product_uom_qty"/></span></h6>-->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </t>-->
|
||||||
|
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<!-- <thead>-->
|
||||||
|
<!-- <tr>-->
|
||||||
|
<!-- <th scope="col">#</th>-->
|
||||||
|
<!-- <th scope="col">product</th>-->
|
||||||
|
<!-- <th scope="col">qty</th>-->
|
||||||
|
<!-- <th scope="col">subtotal</th>-->
|
||||||
|
<!-- </tr>-->
|
||||||
|
<!-- </thead>-->
|
||||||
|
<tbody>
|
||||||
|
<t t-set="index" t-value="1"/>
|
||||||
|
<t t-foreach="current_field" t-as="order_line">
|
||||||
|
<tr class="o_tag o_tag_color_{{index}}">
|
||||||
|
<th scope="row"><t t-out="index"/></th>
|
||||||
|
<td><t t-out="order_line.name.substr(0,20)"/></td>
|
||||||
|
<td><t t-out="order_line.product_uom_qty"/></td>
|
||||||
|
<td><t t-out="order_line.price_subtotal"/></td>
|
||||||
|
</tr>
|
||||||
|
<t t-set="index" t-value="index+1"/>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-name="owl_demo.field_partner_id">
|
||||||
|
<h5><t t-out="current_field.name"/></h5>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="badge rounded-pill dropdown o_tag o_tag_color_3"><t t-out="current_field.phone"/></span>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<!-- <t t-name="owl_demo.field_order_line">-->
|
||||||
|
<!--<!– <h3><t t-out="record.data.name"/></h3>–>-->
|
||||||
|
<!-- <ul class="list-group">-->
|
||||||
|
<!-- <t t-foreach="record.data.order_line.records" t-as="order_line">-->
|
||||||
|
<!-- <li class="list-group-item d-flex justify-content-between align-items-center">-->
|
||||||
|
<!-- <t t-out="order_line.id"/>-->
|
||||||
|
<!-- <span class="badge rounded-pill">14</span>-->
|
||||||
|
<!-- </li>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
<!-- </ul>-->
|
||||||
|
<!-- </t>-->
|
||||||
|
|
||||||
|
</templates>
|
||||||
97
owl_demo/static/src/js/html_template/template_field.js
Normal file
97
owl_demo/static/src/js/html_template/template_field.js
Normal file
@@ -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);
|
||||||
3
owl_demo/static/src/js/html_template/template_field.scss
Normal file
3
owl_demo/static/src/js/html_template/template_field.scss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.o_field_widget.o_field_template {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
8
owl_demo/static/src/js/html_template/template_field.xml
Normal file
8
owl_demo/static/src/js/html_template/template_field.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="owl_demo.CTemplateField" owl="1">
|
||||||
|
<div t-ref="showTemplate"/>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
25
owl_demo/static/src/js/services/notification.js
Normal file
25
owl_demo/static/src/js/services/notification.js
Normal file
@@ -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);
|
||||||
50
owl_demo/static/src/js/widgets/MyWidget.js
Normal file
50
owl_demo/static/src/js/widgets/MyWidget.js
Normal file
@@ -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) + " <b>"+this.units+"</b>");
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
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) + " <b>"+this.units+"</b>");
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
fieldRegistry.add('show_units', ShowUnitsWidgetField);
|
||||||
10
owl_demo/static/src/xml/MyComponent.xml
Normal file
10
owl_demo/static/src/xml/MyComponent.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="owl_demo.my_component">
|
||||||
|
<div t-on-click="increment">
|
||||||
|
<t t-esc="state.value"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
4
owl_demo/static/src/xml/MyWidget.xml
Normal file
4
owl_demo/static/src/xml/MyWidget.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates>
|
||||||
|
|
||||||
|
</templates>
|
||||||
36
owl_demo/static/src/xml/PartnerOrderSummary.xml
Normal file
36
owl_demo/static/src/xml/PartnerOrderSummary.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="owl_demo.PartnerOrderSummary" owl="1">
|
||||||
|
<div class="center"
|
||||||
|
style="width: 100%; text-align: center; border: 1px solid #cecece; padding: 2rem 20%; margin: 12px 0;">
|
||||||
|
<img
|
||||||
|
t-attf-src="data:image/jpg;base64,{{partner.image_256}}"
|
||||||
|
width="75px"
|
||||||
|
height="75px"
|
||||||
|
style="background-color: #ccc; border-radius: 50%; margin-bottom: 10px;"/>
|
||||||
|
<!-- Customer name -->
|
||||||
|
<p style="font-size: 16px; color: #4d4b4b;"><strong t-esc="partner.name"/></p>
|
||||||
|
<!-- Address -->
|
||||||
|
<p style="font-size: 12px; color: #8c8787;">
|
||||||
|
<i class="fa fa-map-marker" style="padding-right: 4px;"/>
|
||||||
|
<span t-esc="partner.city"/>
|
||||||
|
<span t-esc="partner.zip" style="margin-left: 5px;"/>
|
||||||
|
</p>
|
||||||
|
<!-- Grid of previous order stats -->
|
||||||
|
<div class="row" style="padding-top: 20px;">
|
||||||
|
<div class="col-6" style="border-right: 1px solid #ccc;">
|
||||||
|
<p style="font-size: 20px;">
|
||||||
|
<strong t-esc="partner.sale_order_count"/>
|
||||||
|
</p>
|
||||||
|
<p style="font-size: 12px; color: #8c8787;">Orders</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<p style="font-size: 20px;">
|
||||||
|
<strong t-esc="partner.total_invoiced" t-options='{"widget": "monetary"}'/>
|
||||||
|
</p>
|
||||||
|
<p style="font-size: 12px; color: #8c8787;">Total Sales</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
33
owl_demo/views/views.xml
Normal file
33
owl_demo/views/views.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="sale_order_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">sale.order.form.inherit</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="payment_term_id" position="after">
|
||||||
|
<!-- <field name="create_date" widget="show_units" options="{'units':'UTC'}" string="Create Date"/>-->
|
||||||
|
<!-- <group class="o_partner_order_summary" col="2"/>-->
|
||||||
|
<field name="step_file" widget="Viewer3D" readonly="True"/>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale_order_tree_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">sale.order.tree.inherit</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="partner_id" position="attributes">
|
||||||
|
<attribute name="widget">CTemplate</attribute>
|
||||||
|
<attribute name="options">{'template':'owl_demo.field_partner_id'}</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="team_id" position="before">
|
||||||
|
<field name="order_line" widget="CTemplate" options="{'template':'owl_demo.field_order_line'}"/>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -1,18 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from odoo import fields, models, api
|
from odoo import models
|
||||||
import time
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Common(models.Model):
|
class Common(models.Model):
|
||||||
_name = 'sf.sync.common'
|
_name = 'sf.sync.common'
|
||||||
_description = u'公用类'
|
_description = u'公用类'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_headers(self,token, secret_key):
|
def get_headers(self,token, secret_key):
|
||||||
'''
|
'''
|
||||||
获取requests中的heardes参数
|
获取requests中的heardes参数
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
from odoo import fields, models, api
|
from odoo import fields, models, api
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
from odoo.http import request
|
|
||||||
from odoo.addons.sf_base.commons.common import Common
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import logging
|
import logging
|
||||||
from odoo import fields, models, api
|
from odoo import fields, models
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class Sf_Bf_Connect(http.Controller):
|
|||||||
cors="*")
|
cors="*")
|
||||||
def get_bfm_process_order_list(self, **kw):
|
def get_bfm_process_order_list(self, **kw):
|
||||||
"""
|
"""
|
||||||
获取业务平台传送来的订单
|
接收业务平台加工订单分配工厂时传送来的订单数据并生成销售订单和产品及胚料
|
||||||
:param kw:
|
:param kw:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@@ -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(
|
product_bom_purchase.with_user(request.env.ref("base.user_admin")).bom_create_line_has(
|
||||||
purchase_embryo)
|
purchase_embryo)
|
||||||
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
|
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
|
i += 1
|
||||||
res['factory_order_no'] = order_id.name
|
res['factory_order_no'] = order_id.name
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ class ResMrpBom(models.Model):
|
|||||||
'bom_id': self.id,
|
'bom_id': self.id,
|
||||||
'product_id': raw_bom_line.id,
|
'product_id': raw_bom_line.id,
|
||||||
'product_tmpl_id': raw_bom_line.product_tmpl_id.id,
|
'product_tmpl_id': raw_bom_line.product_tmpl_id.id,
|
||||||
'product_qty': round(embryo.volume / 1000000000 * raw_bom_line.materials_type_id.density, 2),
|
'product_qty': round(embryo.volume * raw_bom_line.materials_type_id.density / 1000000),
|
||||||
'product_uom_id': raw_bom_line.uom_id.id,
|
'product_uom_id': raw_bom_line.uom_id.id,
|
||||||
})
|
})
|
||||||
return bom_line
|
return bom_line
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from odoo import api, fields, models
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ResMrpWorkOrder(models.Model):
|
class ResMrpWorkOrder(models.Model):
|
||||||
|
|||||||
@@ -10,12 +10,12 @@
|
|||||||
<!-- <field name="upload_model_file" required="True"-->
|
<!-- <field name="upload_model_file" required="True"-->
|
||||||
<!-- widget='many2many_binary'/>-->
|
<!-- widget='many2many_binary'/>-->
|
||||||
<!-- </field>-->
|
<!-- </field>-->
|
||||||
|
|
||||||
<field name="invoice_policy" position="after">
|
<field name="invoice_policy" position="after">
|
||||||
<!-- <field name="model_file" widget="model_viewer"-->
|
<field name='categ_type' invisible="1"/>
|
||||||
<!-- attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>-->
|
<field name="model_file" widget="model_viewer"
|
||||||
<!-- <field name="embryo_model_type_id" string="模型类型"-->
|
attrs="{'invisible': ['|', ('categ_type', '!=', '成品'),('categ_type', '=', False)]}"/>
|
||||||
<!-- attrs="{'invisible': [('categ_type', '=', '胚料')]}"/>-->
|
<field name="embryo_model_type_id" string="模型类型"
|
||||||
|
attrs="{'invisible': ['|',('categ_type', '!=', '胚料'),('categ_type', '=', False)]}"/>
|
||||||
<field name="materials_id" string="材料"/>
|
<field name="materials_id" string="材料"/>
|
||||||
<field name="materials_type_id" string="型号"
|
<field name="materials_type_id" string="型号"
|
||||||
domain="[('materials_id', '=', materials_id)]"/>
|
domain="[('materials_id', '=', materials_id)]"/>
|
||||||
|
|||||||
@@ -24,18 +24,33 @@ class ResMrpRoutingWorkcenter(models.Model):
|
|||||||
|
|
||||||
company_id = fields.Many2one('res.company', compute="get_company_id", related=False)
|
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 workcenter_ids:
|
||||||
if len(workcenter_ids) == 1:
|
if len(workcenter_ids) == 1:
|
||||||
return workcenter_ids[0]
|
return workcenter_ids[0]
|
||||||
elif len(workcenter_ids) >= 2:
|
elif len(workcenter_ids) >= 2:
|
||||||
# workcenter_ids_str = ','.join([str(s) for s in workcenter_ids])
|
# workcenter_ids_str = ','.join([str(s) for s in workcenter_ids])
|
||||||
|
if routing_type == 'CNC加工':
|
||||||
|
workcenter = self.env['mrp.workcenter'].search([('id', 'in', workcenter_ids)])
|
||||||
|
workcenter_ids = []
|
||||||
|
for item in workcenter:
|
||||||
|
print(item.name)
|
||||||
|
if item.machine_tool_id:
|
||||||
|
machine_tool = self.env['sf.machine_tool'].search(
|
||||||
|
[('x_axis', '>', product.bom_ids.bom_line_ids.product_id.length), ('y_axis', '>', product.bom_ids.bom_line_ids.product_id.width),
|
||||||
|
('z_axis', '>', product.bom_ids.bom_line_ids.product_id.height), ('id', '=', item.machine_tool_id.id)])
|
||||||
|
if machine_tool:
|
||||||
|
workcenter_ids.append(item.id)
|
||||||
|
if len(workcenter_ids) == 1:
|
||||||
|
return workcenter_ids[0]
|
||||||
self.env.cr.execute("""
|
self.env.cr.execute("""
|
||||||
SELECT workcenter_id FROM mrp_workorder where workcenter_id
|
SELECT workcenter_id FROM mrp_workorder where workcenter_id
|
||||||
in %s group by workcenter_id
|
in %s group by workcenter_id
|
||||||
order by count(*),workcenter_id asc limit 1 """, [tuple(workcenter_ids)])
|
order by count(*),workcenter_id asc limit 1 """, [tuple(workcenter_ids)])
|
||||||
return self.env.cr.dictfetchall()[0].get('workcenter_id')
|
workcenter_id = self.env.cr.dictfetchall()[0].get('workcenter_id')
|
||||||
|
return workcenter_id
|
||||||
|
|||||||
@@ -146,7 +146,9 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
'processing_panel': k,
|
'processing_panel': k,
|
||||||
'routing_type': route.routing_type,
|
'routing_type': route.routing_type,
|
||||||
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
|
'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_start': False,
|
||||||
'date_planned_finished': False,
|
'date_planned_finished': False,
|
||||||
'duration_expected': 60,
|
'duration_expected': 60,
|
||||||
@@ -357,7 +359,9 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
'processing_panel': k,
|
'processing_panel': k,
|
||||||
'routing_type': route.routing_type,
|
'routing_type': route.routing_type,
|
||||||
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
|
'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_start': False,
|
||||||
'date_planned_finished': False,
|
'date_planned_finished': False,
|
||||||
'duration_expected': 60,
|
'duration_expected': 60,
|
||||||
@@ -463,6 +467,7 @@ class CNCprocessing(models.Model):
|
|||||||
cnc_workorder.time_ids.date_end = datetime.now()
|
cnc_workorder.time_ids.date_end = datetime.now()
|
||||||
cnc_workorder.button_finish()
|
cnc_workorder.button_finish()
|
||||||
|
|
||||||
|
# 根据程序名和加工面匹配到ftp里对应的Nc程序名
|
||||||
def get_cnc_processing_file(self, folder_name, cnc_processing, processing_panel):
|
def get_cnc_processing_file(self, folder_name, cnc_processing, processing_panel):
|
||||||
logging.info('folder_name:%s' % folder_name)
|
logging.info('folder_name:%s' % folder_name)
|
||||||
serverdir = os.path.join('/tmp', folder_name, 'return', processing_panel)
|
serverdir = os.path.join('/tmp', folder_name, 'return', processing_panel)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from odoo import SUPERUSER_ID, _, api, fields, models, registry
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
class Users(models.Model):
|
class Users(models.Model):
|
||||||
_inherit = 'res.users'
|
_inherit = 'res.users'
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import logging
|
|
||||||
from collections import defaultdict, namedtuple
|
from collections import defaultdict, namedtuple
|
||||||
from odoo.addons.stock.models.stock_rule import ProcurementException
|
from odoo.addons.stock.models.stock_rule import ProcurementException
|
||||||
from re import findall as regex_findall
|
from re import findall as regex_findall
|
||||||
from re import split as regex_split
|
from re import split as regex_split
|
||||||
from odoo import SUPERUSER_ID, _, api, fields, models, registry
|
from odoo import SUPERUSER_ID, _, api, models
|
||||||
from odoo.tools import float_compare, float_is_zero, html_escape
|
from odoo.tools import float_compare
|
||||||
|
|
||||||
|
|
||||||
class StockRule(models.Model):
|
class StockRule(models.Model):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
||||||
import base64
|
import base64
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
from odoo import api, fields, models
|
||||||
from pystrich.code128 import Code128Encoder
|
from pystrich.code128 import Code128Encoder
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import posixpath
|
|
||||||
from odoo.modules import get_resource_path
|
|
||||||
from ftplib import FTP
|
from ftplib import FTP
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from odoo.exceptions import UserError
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
|
import base64
|
||||||
from odoo import models
|
from odoo import models
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
import logging
|
import logging
|
||||||
from odoo.addons.sf_base.commons.common import Common
|
from odoo.addons.sf_base.commons.common import Common
|
||||||
|
|
||||||
from odoo.http import request
|
|
||||||
from .res_config_setting import ResConfigSettings
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user