Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造订单报废流程
# Conflicts: # sf_manufacturing/models/mrp_production.py
@@ -1,7 +1,7 @@
|
|||||||
.o_data_row .w-100 {
|
.o_data_row .w-100 {
|
||||||
width: 40px !important;
|
width: 40px !important;
|
||||||
height: 40px !important;
|
height: 40px !important;
|
||||||
display: block !important;
|
//display: block !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) {
|
.o_list_renderer .o_list_table tbody > tr > td:not(.o_list_record_selector):not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) {
|
||||||
|
|||||||
292
sf_dlm/models/product_template.py
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
from odoo import models, fields
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
|
class ResProductTemplate(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
|
||||||
|
# 模型的长,宽,高,体积,精度,材料
|
||||||
|
model_name = fields.Char('模型名称')
|
||||||
|
categ_type = fields.Selection(
|
||||||
|
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")], string='产品的类别', related='categ_id.type', store=True)
|
||||||
|
model_long = fields.Float('模型长[mm]', digits=(16, 3))
|
||||||
|
model_width = fields.Float('模型宽[mm]', digits=(16, 3))
|
||||||
|
model_height = fields.Float('模型高[mm]', digits=(16, 3))
|
||||||
|
model_volume = fields.Float('模型体积[m³]')
|
||||||
|
model_machining_precision = fields.Selection([
|
||||||
|
('0.10', '±0.10mm'),
|
||||||
|
('0.05', '±0.05mm'),
|
||||||
|
('0.03', '±0.03mm'),
|
||||||
|
('0.02', '±0.02mm'),
|
||||||
|
('0.01', '±0.01mm')], string='加工精度')
|
||||||
|
product_model_type_id = fields.Many2one('sf.model.type', string='产品模型类型')
|
||||||
|
embryo_model_type_id = fields.Many2one('sf.model.type', string='胚料模型类型')
|
||||||
|
model_processing_panel = fields.Char('模型加工面板')
|
||||||
|
model_surface_process_id = fields.Many2one('sf.production.process', string='表面工艺')
|
||||||
|
model_process_parameters_id = fields.Many2one('sf.processing.technology', string='工艺参数')
|
||||||
|
# model_price = fields.Float('模型单价', digits=(16, 3))
|
||||||
|
model_remark = fields.Char('模型备注说明')
|
||||||
|
length = fields.Float('长[mm]', digits=(16, 3))
|
||||||
|
width = fields.Float('宽[mm]', digits=(16, 3))
|
||||||
|
height = fields.Float('高[mm]', digits=(16, 3))
|
||||||
|
materials_id = fields.Many2one('sf.production.materials', string='材料')
|
||||||
|
materials_type_id = fields.Many2one('sf.materials.model', string='材料型号')
|
||||||
|
single_manufacturing = fields.Boolean(string="单个制造")
|
||||||
|
upload_model_file = fields.Many2many('ir.attachment', 'upload_model_file_attachment_ref', string='上传模型文件')
|
||||||
|
model_code = fields.Char('模型编码')
|
||||||
|
is_bfm = fields.Boolean('业务平台是否自动创建', default=False)
|
||||||
|
|
||||||
|
def _get_volume_uom_id_from_ir_config_parameter(self):
|
||||||
|
product_length_in_feet_param = self.env['ir.config_parameter'].sudo().get_param('product.volume_in_cubic_feet')
|
||||||
|
if product_length_in_feet_param == '1':
|
||||||
|
return self.env.ref('uom.product_uom_cubic_foot')
|
||||||
|
else:
|
||||||
|
return self.env.ref('sf_dlm.product_uom_cubic_millimeter')
|
||||||
|
|
||||||
|
# model_file = fields.Binary('模型文件')
|
||||||
|
|
||||||
|
# 胚料的库存路线设置
|
||||||
|
# def _get_routes(self, route_type):
|
||||||
|
# route_manufacture = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False).sudo()
|
||||||
|
# route_mto = self.env.ref('stock.route_warehouse0_mto', raise_if_not_found=False).sudo()
|
||||||
|
# route_purchase = self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False).sudo()
|
||||||
|
# if route_manufacture and route_mto:
|
||||||
|
# # 外协
|
||||||
|
# if route_type == 'subcontract':
|
||||||
|
# route_subcontract = self.env.ref('mrp_subcontracting.route_resupply_subcontractor_mto',
|
||||||
|
# raise_if_not_found=False).sudo()
|
||||||
|
# return [route_mto.id, route_purchase.id, route_subcontract.id]
|
||||||
|
# elif route_type == 'purchase':
|
||||||
|
# # 采购
|
||||||
|
# return [route_mto.id, route_purchase.id]
|
||||||
|
# else:
|
||||||
|
# return [route_mto.id, route_manufacture.id]
|
||||||
|
# return []
|
||||||
|
|
||||||
|
# route_ids = fields.Many2many(default=lambda self: self._get_route())
|
||||||
|
|
||||||
|
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品
|
||||||
|
def product_create(self, product_id, item, order_id, order_number, i):
|
||||||
|
copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
|
||||||
|
copy_product_id.product_tmpl_id.active = True
|
||||||
|
model_type = self.env['sf.model.type'].search([], limit=1)
|
||||||
|
attachment = self.attachment_create(item['model_name'], item['model_data'])
|
||||||
|
vals = {
|
||||||
|
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
||||||
|
'model_long': item['model_long'] + model_type.embryo_tolerance,
|
||||||
|
'model_width': item['model_width'] + model_type.embryo_tolerance,
|
||||||
|
'model_height': item['model_height'] + model_type.embryo_tolerance,
|
||||||
|
'model_volume': (item['model_long'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_width'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_height'] + model_type.embryo_tolerance),
|
||||||
|
'product_model_type_id': model_type.id,
|
||||||
|
'model_processing_panel': 'R',
|
||||||
|
'model_machining_precision': item['model_machining_precision'],
|
||||||
|
'model_code': item['barcode'],
|
||||||
|
'length': item['model_long'],
|
||||||
|
'width': item['model_width'],
|
||||||
|
'height': item['model_height'],
|
||||||
|
'volume': item['model_long'] * item['model_width'] * item['model_height'],
|
||||||
|
'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
|
||||||
|
'model_name': attachment.name,
|
||||||
|
'upload_model_file': [(6, 0, [attachment.id])],
|
||||||
|
# 'single_manufacturing': True,
|
||||||
|
# 'tracking': 'serial',
|
||||||
|
'list_price': item['price'],
|
||||||
|
# 'categ_id': self.env.ref('sf_dlm.product_category_finished_sf').id,
|
||||||
|
'materials_id': self.env['sf.production.materials'].search(
|
||||||
|
[('materials_no', '=', item['texture_code'])]).id,
|
||||||
|
'materials_type_id': self.env['sf.materials.model'].search(
|
||||||
|
[('materials_no', '=', item['texture_type_code'])]).id,
|
||||||
|
'model_surface_process_id': self.env['sf.production.process'].search(
|
||||||
|
[('process_encode', '=', item['surface_process_code'])]).id,
|
||||||
|
# 'model_process_parameters_id': self.env['sf.processing.technology'].search(
|
||||||
|
# [('process_encode', '=', item['process_parameters_code'])]).id,
|
||||||
|
'model_remark': item['remark'],
|
||||||
|
'default_code': '%s-%s' % (order_number, i),
|
||||||
|
# 'barcode': item['barcode'],
|
||||||
|
'active': True,
|
||||||
|
# 'route_ids': self._get_routes('')
|
||||||
|
}
|
||||||
|
copy_product_id.sudo().write(vals)
|
||||||
|
# product_id.product_tmpl_id.active = False
|
||||||
|
return copy_product_id
|
||||||
|
|
||||||
|
def attachment_create(self, name, data):
|
||||||
|
attachment = self.env['ir.attachment'].create({
|
||||||
|
'datas': base64.b64decode(data),
|
||||||
|
'type': 'binary',
|
||||||
|
'public': True,
|
||||||
|
'description': '模型文件',
|
||||||
|
'name': name
|
||||||
|
})
|
||||||
|
return attachment
|
||||||
|
|
||||||
|
# 创建胚料
|
||||||
|
def no_bom_product_create(self, product_id, item, order_id, route_type, i):
|
||||||
|
no_bom_copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
|
||||||
|
no_bom_copy_product_id.product_tmpl_id.active = True
|
||||||
|
materials_id = self.env['sf.production.materials'].search(
|
||||||
|
[('materials_no', '=', item['texture_code'])])
|
||||||
|
materials_type_id = self.env['sf.materials.model'].search(
|
||||||
|
[('materials_no', '=', item['texture_type_code'])])
|
||||||
|
model_type = self.env['sf.model.type'].search([], limit=1)
|
||||||
|
supplier = self.env['mrp.bom'].get_supplier(materials_type_id)
|
||||||
|
logging.info('no_bom_copy_product_supplier-vals:%s' % supplier)
|
||||||
|
vals = {
|
||||||
|
'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R',
|
||||||
|
order_id.name, i, materials_id.name, materials_type_id.name,
|
||||||
|
item['model_long'] + model_type.embryo_tolerance,
|
||||||
|
item['model_width'] + model_type.embryo_tolerance,
|
||||||
|
item['model_height'] + model_type.embryo_tolerance),
|
||||||
|
'length': item['model_long'] + model_type.embryo_tolerance,
|
||||||
|
'width': item['model_width'] + model_type.embryo_tolerance,
|
||||||
|
'height': item['model_height'] + model_type.embryo_tolerance,
|
||||||
|
'volume': (item['model_long'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_width'] + model_type.embryo_tolerance) * (
|
||||||
|
item['model_height'] + model_type.embryo_tolerance),
|
||||||
|
'embryo_model_type_id': model_type.id,
|
||||||
|
'list_price': item['price'],
|
||||||
|
'materials_id': materials_id.id,
|
||||||
|
'materials_type_id': materials_type_id.id,
|
||||||
|
'is_bfm': True,
|
||||||
|
# 'route_ids': self._get_routes(route_type),
|
||||||
|
# 'categ_id': self.env.ref('sf_dlm.product_category_embryo_sf').id,
|
||||||
|
# 'model_surface_process_id': self.env['sf.production.process'].search(
|
||||||
|
# [('process_encode', '=', item['surface_process_code'])]).id,
|
||||||
|
# 'model_process_parameters_id': self.env['sf.processing.technology'].search(
|
||||||
|
# [('process_encode', '=', item['process_parameters_code'])]).id,
|
||||||
|
'active': True
|
||||||
|
}
|
||||||
|
# 外协和采购生成的胚料需要根据材料型号绑定供应商
|
||||||
|
if route_type == 'subcontract' or route_type == 'purchase':
|
||||||
|
no_bom_copy_product_id.purchase_ok = True
|
||||||
|
no_bom_copy_product_id.seller_ids = [
|
||||||
|
(0, 0, {'partner_id': supplier.partner_id.id, 'delay': 1.0})]
|
||||||
|
if route_type == 'subcontract':
|
||||||
|
partner = self.env['res.partner'].search([('id', '=', supplier.partner_id.id)])
|
||||||
|
partner.is_subcontractor = True
|
||||||
|
no_bom_copy_product_id.write(vals)
|
||||||
|
logging.info('no_bom_copy_product_id-vals:%s' % vals)
|
||||||
|
# product_id.product_tmpl_id.active = False
|
||||||
|
return no_bom_copy_product_id
|
||||||
|
|
||||||
|
# @api.onchange('upload_model_file')
|
||||||
|
# def onchange_model_file(self):
|
||||||
|
# for item in self:
|
||||||
|
# if len(item.upload_model_file) > 1:
|
||||||
|
# raise ValidationError('只允许上传一个文件')
|
||||||
|
# if item.upload_model_file:
|
||||||
|
# file_attachment_id = item.upload_model_file[0]
|
||||||
|
# item.model_name = file_attachment_id.name
|
||||||
|
# # 附件路径
|
||||||
|
# report_path = file_attachment_id._full_path(file_attachment_id.store_fname)
|
||||||
|
# shapes = read_step_file(report_path)
|
||||||
|
# output_file = get_resource_path('sf_dlm', 'static/file', 'out.stl')
|
||||||
|
# write_stl_file(shapes, output_file, 'binary', 0.03, 0.5)
|
||||||
|
# # 转化为glb
|
||||||
|
# output_glb_file = get_resource_path('sf_dlm', 'static/file', 'out.glb')
|
||||||
|
# util_path = get_resource_path('sf_dlm', 'static/util')
|
||||||
|
# cmd = 'python %s/stl2gltf.py %s %s -b' % (util_path, output_file, output_glb_file)
|
||||||
|
# os.system(cmd)
|
||||||
|
# # 转base64
|
||||||
|
# with open(output_glb_file, 'rb') as fileObj:
|
||||||
|
# image_data = fileObj.read()
|
||||||
|
# base64_data = base64.b64encode(image_data)
|
||||||
|
# item.model_file = base64_data
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpBom(models.Model):
|
||||||
|
_inherit = 'mrp.bom'
|
||||||
|
|
||||||
|
subcontractor_id = fields.Many2one('res.partner', string='外包商')
|
||||||
|
|
||||||
|
def bom_create_line_has(self, embryo):
|
||||||
|
vals = {
|
||||||
|
'bom_id': self.id,
|
||||||
|
'product_id': embryo.id,
|
||||||
|
'product_tmpl_id': embryo.product_tmpl_id.id,
|
||||||
|
'product_qty': 1,
|
||||||
|
'product_uom_id': 1
|
||||||
|
}
|
||||||
|
return self.env['mrp.bom.line'].create(vals)
|
||||||
|
|
||||||
|
# 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom
|
||||||
|
def bom_create(self, product, bom_type, product_type):
|
||||||
|
bom_id = self.env['mrp.bom'].create({
|
||||||
|
'product_tmpl_id': product.product_tmpl_id.id,
|
||||||
|
'type': bom_type,
|
||||||
|
# 'subcontractor_id': '' or subcontract.partner_id.id,
|
||||||
|
'product_qty': 1,
|
||||||
|
'product_uom_id': 1
|
||||||
|
})
|
||||||
|
if bom_type == 'subcontract' and product_type is not False:
|
||||||
|
subcontract = self.get_supplier(product.materials_type_id)
|
||||||
|
bom_id.subcontractor_id = subcontract.partner_id.id
|
||||||
|
return bom_id
|
||||||
|
|
||||||
|
# 胚料BOM组件:选取当前胚料原材料,
|
||||||
|
# 然后根据当前的胚料的体积得出需要的原材料重量(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤)
|
||||||
|
# 胚料所需原材料公式:当前的胚料的体积(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤)
|
||||||
|
def bom_create_line(self, embryo):
|
||||||
|
# 选取当前胚料原材料
|
||||||
|
raw_bom_line = self.get_raw_bom(embryo)
|
||||||
|
if raw_bom_line:
|
||||||
|
bom_line = self.env['mrp.bom.line'].create({
|
||||||
|
'bom_id': self.id,
|
||||||
|
'product_id': raw_bom_line.id,
|
||||||
|
'product_tmpl_id': raw_bom_line.product_tmpl_id.id,
|
||||||
|
'product_qty': round(embryo.volume * raw_bom_line.materials_type_id.density / 1000000),
|
||||||
|
'product_uom_id': raw_bom_line.uom_id.id,
|
||||||
|
})
|
||||||
|
return bom_line
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 查询材料型号默认排第一的供应商
|
||||||
|
def get_supplier(self, materials_type):
|
||||||
|
seller_id = self.env['sf.supplier.sort'].search(
|
||||||
|
[('materials_model_id', '=', materials_type.id)],
|
||||||
|
limit=1,
|
||||||
|
order='sequence asc')
|
||||||
|
return seller_id
|
||||||
|
|
||||||
|
# 匹配bom
|
||||||
|
def get_bom(self, product):
|
||||||
|
embryo_has = self.env['product.product'].search(
|
||||||
|
[('categ_id.type', '=', '胚料'), ('materials_type_id', '=', product.materials_type_id.id),
|
||||||
|
('length', '>', product.length), ('width', '>', product.width),
|
||||||
|
('height', '>', product.height), ('is_bfm', '=', False)
|
||||||
|
],
|
||||||
|
limit=1,
|
||||||
|
order='volume desc'
|
||||||
|
)
|
||||||
|
logging.info('get_bom-vals:%s' % embryo_has)
|
||||||
|
if embryo_has:
|
||||||
|
rate_of_waste = ((embryo_has.volume - product.model_volume) % embryo_has.volume) * 100
|
||||||
|
if rate_of_waste <= 20:
|
||||||
|
return embryo_has
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 查bom的原材料
|
||||||
|
def get_raw_bom(self, product):
|
||||||
|
raw_bom = self.env['product.product'].search(
|
||||||
|
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)])
|
||||||
|
return raw_bom
|
||||||
|
|
||||||
|
|
||||||
|
class ResProductCategory(models.Model):
|
||||||
|
_inherit = "product.category"
|
||||||
|
|
||||||
|
type = fields.Selection(
|
||||||
|
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")],
|
||||||
|
default="", string="类型")
|
||||||
|
|
||||||
|
# @api.constrains('type')
|
||||||
|
# def _check_type(self):
|
||||||
|
# category = self.env['product.category'].search(
|
||||||
|
# [('type', '=', self.type)])
|
||||||
|
# if category:
|
||||||
|
# raise ValidationError("该类别已存在,请选择其他类别")
|
||||||
13
sf_dlm/models/product_workorder.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpWorkOrder(models.Model):
|
||||||
|
_inherit = 'mrp.workorder'
|
||||||
|
_order = 'sequence'
|
||||||
|
|
||||||
|
product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True, check_company=True, string="胚料长度(mm)")
|
||||||
|
product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True, check_company=True, string="胚料宽度(mm)")
|
||||||
|
product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True, check_company=True, string="胚料高度(mm)")
|
||||||
|
product_tmpl_id_materials_id = fields.Many2one(related='production_id.product_tmpl_id.materials_id', readonly=True, store=True, check_company=True, string="材料")
|
||||||
|
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id', readonly=True, store=True, check_company=True, string="型号")
|
||||||
BIN
sf_dlm/static/file/out.glb
Normal file
BIN
sf_dlm/static/file/out.stl
Normal file
277
sf_dlm/static/util/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)
|
||||||
27
sf_dlm/views/product_workorder.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_mrp_production_workorder_tray_form_inherit_sf1" model="ir.ui.view">
|
||||||
|
<field name="name">production.workorder.dlm</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="sf_manufacturing.view_mrp_production_workorder_tray_form_inherit_sf"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[1]" position="before">
|
||||||
|
<page string="开料要求" attrs='{"invisible": [("routing_type","!=","切割")]}'>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="product_tmpl_id_materials_id" widget="many2one"/>
|
||||||
|
<field name="product_tmpl_id_materials_type_id" widget="many2one"/>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="product_tmpl_id_length"/>
|
||||||
|
<field name="product_tmpl_id_width"/>
|
||||||
|
<field name="product_tmpl_id_height"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
82
sf_machine_connect/static/src/js/test.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { browser } from "@web/core/browser/browser";
|
||||||
|
import { Dialog } from "@web/core/dialog/dialog";
|
||||||
|
import { _lt } from "@web/core/l10n/translation";
|
||||||
|
import { useChildRef, useOwnedDialogs, useService } from "@web/core/utils/hooks";
|
||||||
|
import { sprintf } from "@web/core/utils/strings";
|
||||||
|
import { isMobileOS } from "@web/core/browser/feature_detection";
|
||||||
|
import * as BarcodeScanner from "@web/webclient/barcode/barcode_scanner";
|
||||||
|
|
||||||
|
const {xml, Component} = owl;
|
||||||
|
import { standardFieldProps } from "@web/views/fields/standard_field_props";
|
||||||
|
// Import the registry
|
||||||
|
import {registry} from "@web/core/registry";
|
||||||
|
|
||||||
|
|
||||||
|
export class CodeField extends Component {
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
async onBarcodeBtnClick() {
|
||||||
|
const barcode = await BarcodeScanner.scanBarcode();
|
||||||
|
if (barcode) {
|
||||||
|
await this.onBarcodeScanned(barcode);
|
||||||
|
if ("vibrate" in browser.navigator) {
|
||||||
|
browser.navigator.vibrate(100);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.notification.add(this.env._t("Please, scan again !"), {
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async search(barcode) {
|
||||||
|
const results = await this.orm.call("sf.tray", "name_search", [code], {
|
||||||
|
name: barcode,
|
||||||
|
args: this.getDomain(),
|
||||||
|
operator: "ilike",
|
||||||
|
limit: 2, // If one result we set directly and if more than one we use normal flow so no need to search more
|
||||||
|
context: this.context,
|
||||||
|
});
|
||||||
|
return results.map((result) => {
|
||||||
|
const [id, displayName] = result;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async onBarcodeScanned(barcode) {
|
||||||
|
const results = await this.search(barcode);
|
||||||
|
const records = results.filter((r) => !!r.id);
|
||||||
|
if (records.length === 1) {
|
||||||
|
this.update([{ id: records[0].id, name: records[0].name }]);
|
||||||
|
} else {
|
||||||
|
const searchInput = this.autocompleteContainerRef.el.querySelector("input");
|
||||||
|
searchInput.value = barcode;
|
||||||
|
searchInput.dispatchEvent(new Event("input"));
|
||||||
|
if (this.env.isSmall) {
|
||||||
|
searchInput.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeField.template = xml`
|
||||||
|
<button
|
||||||
|
t-on-click="onBarcodeBtnClick"
|
||||||
|
type="button"
|
||||||
|
class="btn ms-3 o_barcode"
|
||||||
|
tabindex="-1"
|
||||||
|
draggable="false"
|
||||||
|
aria-label="Scan barcode"
|
||||||
|
title="Scan barcode"
|
||||||
|
data-tooltip="Scan barcode"
|
||||||
|
/>
|
||||||
|
`;
|
||||||
|
// CodeField.template = 'sf_machine_connect.CodeField';
|
||||||
|
CodeField.props = standardFieldProps;
|
||||||
|
|
||||||
|
// Add the field to the correct category
|
||||||
|
registry.category("fields").add("code", CodeField);
|
||||||
@@ -466,7 +466,6 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
order='id asc')
|
order='id asc')
|
||||||
if workpiece_delivery:
|
if workpiece_delivery:
|
||||||
for wd in workpiece_delivery:
|
for wd in workpiece_delivery:
|
||||||
logging.info('wd.production_id:%s' % wd.production_id.name)
|
|
||||||
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '待上产线':
|
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '待上产线':
|
||||||
logging.info(
|
logging.info(
|
||||||
'wd.production_line_state:%s' % wd.production_id.production_line_state)
|
'wd.production_line_state:%s' % wd.production_id.production_line_state)
|
||||||
@@ -519,7 +518,6 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
order='id asc')
|
order='id asc')
|
||||||
if workpiece_delivery:
|
if workpiece_delivery:
|
||||||
for wd in workpiece_delivery:
|
for wd in workpiece_delivery:
|
||||||
logging.info('wd.production_id:%s' % wd.production_id.name)
|
|
||||||
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '已上产线':
|
if wd.workorder_id.state == 'done' and wd.production_id.production_line_state == '已上产线':
|
||||||
logging.info(
|
logging.info(
|
||||||
'wd.production_line_state:%s' % wd.production_id.production_line_state)
|
'wd.production_line_state:%s' % wd.production_id.production_line_state)
|
||||||
|
|||||||
@@ -408,12 +408,12 @@ class MrpProduction(models.Model):
|
|||||||
workorders_values.append(
|
workorders_values.append(
|
||||||
self.env['mrp.workorder'].json_workorder_str('', production, route))
|
self.env['mrp.workorder'].json_workorder_str('', production, route))
|
||||||
production.workorder_ids = workorders_values
|
production.workorder_ids = workorders_values
|
||||||
if is_fetchcnc is False and scrap_production:
|
if production_programming.programming_state == '已编程':
|
||||||
production.write({'programming_no': scrap_production.programming_no,
|
logging.info("production_programming: %s" % production_programming.name)
|
||||||
'programming_state': '已编程'})
|
|
||||||
production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').write({
|
production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').write({
|
||||||
'cnc_ids': scrap_production.workorder_ids.filtered(
|
'cnc_ids': production_programming.workorder_ids.filtered(
|
||||||
lambda t1: t1.routing_type == 'CNC加工').cnc_ids})
|
lambda
|
||||||
|
t1: t1.routing_type == 'CNC加工').cnc_ids})
|
||||||
for workorder in production.workorder_ids:
|
for workorder in production.workorder_ids:
|
||||||
workorder.duration_expected = workorder._get_duration_expected()
|
workorder.duration_expected = workorder._get_duration_expected()
|
||||||
|
|
||||||
@@ -507,8 +507,7 @@ class MrpProduction(models.Model):
|
|||||||
|
|
||||||
def _reset_work_order_sequence(self):
|
def _reset_work_order_sequence(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
product_routing_sequence_list = {} # 成品
|
sequence_list = {}
|
||||||
embryo_routing_sequence_list = {} # 坯料
|
|
||||||
model_type_id = rec.product_id.product_model_type_id
|
model_type_id = rec.product_id.product_model_type_id
|
||||||
if model_type_id:
|
if model_type_id:
|
||||||
tmpl_num = 1
|
tmpl_num = 1
|
||||||
@@ -516,7 +515,7 @@ class MrpProduction(models.Model):
|
|||||||
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
||||||
if product_routing_tmpl_ids:
|
if product_routing_tmpl_ids:
|
||||||
for tmpl_id in product_routing_tmpl_ids:
|
for tmpl_id in product_routing_tmpl_ids:
|
||||||
product_routing_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||||
tmpl_num += 1
|
tmpl_num += 1
|
||||||
# 表面工艺工序
|
# 表面工艺工序
|
||||||
# 模型类型的表面工艺工序模版
|
# 模型类型的表面工艺工序模版
|
||||||
@@ -527,7 +526,6 @@ class MrpProduction(models.Model):
|
|||||||
if model_process_parameters_ids:
|
if model_process_parameters_ids:
|
||||||
for process_parameters_id in model_process_parameters_ids:
|
for process_parameters_id in model_process_parameters_ids:
|
||||||
process_id = process_parameters_id.process_id
|
process_id = process_parameters_id.process_id
|
||||||
surface_tmpl_name = ''
|
|
||||||
for surface_tmpl_id in surface_tmpl_ids:
|
for surface_tmpl_id in surface_tmpl_ids:
|
||||||
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
|
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
|
||||||
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
|
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
|
||||||
@@ -535,30 +533,23 @@ class MrpProduction(models.Model):
|
|||||||
surface_tmpl_name, process_parameters_id.name)})
|
surface_tmpl_name, process_parameters_id.name)})
|
||||||
process_list = sorted(process_dict.keys())
|
process_list = sorted(process_dict.keys())
|
||||||
for process_num in process_list:
|
for process_num in process_list:
|
||||||
product_routing_sequence_list.update({process_dict.get(process_num): tmpl_num})
|
sequence_list.update({process_dict.get(process_num): tmpl_num})
|
||||||
tmpl_num += 1
|
tmpl_num += 1
|
||||||
# 坯料工序
|
# 坯料工序
|
||||||
tmpl_num = 1
|
tmpl_num = 1
|
||||||
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
|
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
|
||||||
if embryo_routing_tmpl_ids:
|
if embryo_routing_tmpl_ids:
|
||||||
for tmpl_id in embryo_routing_tmpl_ids:
|
for tmpl_id in embryo_routing_tmpl_ids:
|
||||||
embryo_routing_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||||
tmpl_num += 1
|
tmpl_num += 1
|
||||||
else:
|
else:
|
||||||
raise ValidationError('该产品没有选择【模版类型】!')
|
raise ValidationError('该产品没有选择【模版类型】!')
|
||||||
|
|
||||||
if rec.product_id.categ_id.name == '成品':
|
for work in rec.workorder_ids:
|
||||||
for work in rec.workorder_ids:
|
if sequence_list.get(work.name):
|
||||||
if product_routing_sequence_list.get(work.name):
|
work.sequence = sequence_list[work.name]
|
||||||
work.sequence = product_routing_sequence_list[work.name]
|
else:
|
||||||
else:
|
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
||||||
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
|
||||||
elif rec.product_id.categ_id.name == '坯料':
|
|
||||||
for work in rec.workorder_ids:
|
|
||||||
if embryo_routing_sequence_list.get(work.name):
|
|
||||||
work.sequence = embryo_routing_sequence_list[work.name]
|
|
||||||
else:
|
|
||||||
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
|
||||||
# if work.name == '获取CNC加工程序':
|
# if work.name == '获取CNC加工程序':
|
||||||
# work.button_start()
|
# work.button_start()
|
||||||
# #work.fetchCNC()
|
# #work.fetchCNC()
|
||||||
|
|||||||
@@ -1348,9 +1348,18 @@ class WorkPieceDelivery(models.Model):
|
|||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
|
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
|
||||||
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
|
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
|
||||||
|
else:
|
||||||
|
vals['type'] = '运送空料架'
|
||||||
obj = super(WorkPieceDelivery, self).create(vals)
|
obj = super(WorkPieceDelivery, self).create(vals)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@api.constrains('name')
|
||||||
|
def _check_name(self):
|
||||||
|
if self.type == '运送空料架':
|
||||||
|
wd = self.sudo().search([('name', '=', self.name), ('id', '!=', self.id)])
|
||||||
|
if wd:
|
||||||
|
raise UserError("该名称已存在")
|
||||||
|
|
||||||
def action_delivery_history(self):
|
def action_delivery_history(self):
|
||||||
return {
|
return {
|
||||||
'name': _('配送历史'),
|
'name': _('配送历史'),
|
||||||
@@ -1531,7 +1540,7 @@ class WorkPieceDelivery(models.Model):
|
|||||||
'task_delivery_time': fields.Datetime.now(),
|
'task_delivery_time': fields.Datetime.now(),
|
||||||
'status': '待配送'
|
'status': '待配送'
|
||||||
})
|
})
|
||||||
if delivery_item == "上产线":
|
if delivery_item.type == "上产线":
|
||||||
delivery_item.workorder_id.write({'is_delivery': True})
|
delivery_item.workorder_id.write({'is_delivery': True})
|
||||||
else:
|
else:
|
||||||
raise UserError(ret['message'])
|
raise UserError(ret['message'])
|
||||||
|
|||||||
59
sf_manufacturing/models/tray.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of SmartGo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||||
|
from pystrich.code128 import Code128Encoder
|
||||||
|
|
||||||
|
|
||||||
|
class Tray(models.Model):
|
||||||
|
_inherit = 'sf.tray'
|
||||||
|
_description = '托盘'
|
||||||
|
qr_image = fields.Binary(string="托盘二维码", compute='compute_qr_image')
|
||||||
|
production_id = fields.Many2one('mrp.production', string='制造订单',
|
||||||
|
related='workorder_id.production_id'
|
||||||
|
)
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string="工单"
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.onchange('production_id')
|
||||||
|
def updateTrayState(self):
|
||||||
|
if self.workorder_id != False and self.create_date != False:
|
||||||
|
|
||||||
|
self.state = '占用'
|
||||||
|
else:
|
||||||
|
self.state = '空闲'
|
||||||
|
|
||||||
|
def unclamp(self):
|
||||||
|
self.workorder_id = False
|
||||||
|
self.production_id = False
|
||||||
|
self.state = '空闲'
|
||||||
|
|
||||||
|
@api.depends('code')
|
||||||
|
def compute_qr_image(self):
|
||||||
|
for item in self:
|
||||||
|
if not item.code:
|
||||||
|
item.qr_image = False
|
||||||
|
continue
|
||||||
|
# 根据code动态生成二维码图片
|
||||||
|
# qr = qrcode.QRCode(
|
||||||
|
# version=1,
|
||||||
|
# error_correction=qrcode.constants.ERROR_CORRECT_L,
|
||||||
|
# box_size=10,
|
||||||
|
# border=4,
|
||||||
|
# )
|
||||||
|
# qr.add_data(item.code)
|
||||||
|
# qr.make(fit=True)
|
||||||
|
# img = qr.make_image()
|
||||||
|
# 生成条形码文件
|
||||||
|
# bar = barcode.get("ean13", "123456789102", writer=ImageWriter())
|
||||||
|
# a = bar.get_fullcode()
|
||||||
|
# b = bar.save('occ')
|
||||||
|
# 生成条形码图片
|
||||||
|
partner_encoder = Code128Encoder(item.code)
|
||||||
|
# 转换bytes流
|
||||||
|
temp = BytesIO()
|
||||||
|
partner_encoder.save(temp)
|
||||||
|
# img.save(temp, format='PNG')
|
||||||
|
qr_image = base64.b64encode(temp.getvalue())
|
||||||
|
item.qr_image = qr_image
|
||||||
74
sf_manufacturing/report/tray_report.xml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- 托盘码打印尺寸-->
|
||||||
|
<record id="sf_tray1" model="report.paperformat">
|
||||||
|
<field name="name">Dymo Label Sheet</field>
|
||||||
|
<field name="default" eval="True"/>
|
||||||
|
<field name="format">custom</field>
|
||||||
|
<field name="page_height">100</field>
|
||||||
|
<field name="page_width">60</field>
|
||||||
|
<field name="orientation">Landscape</field>
|
||||||
|
<field name="margin_top">0</field>
|
||||||
|
<field name="margin_bottom">0</field>
|
||||||
|
<field name="margin_left">0</field>
|
||||||
|
<field name="margin_right">0</field>
|
||||||
|
<field name="disable_shrinking" eval="True"/>
|
||||||
|
<field name="dpi">96</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 托盘码打印动作-->
|
||||||
|
<record id="label_sf_tray_code" model="ir.actions.report">
|
||||||
|
<field name="name">打印条形码</field>
|
||||||
|
<field name="model">sf.tray</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">sf_manufacturing.sf_tray_template</field>
|
||||||
|
<field name="report_file">sf_manufacturing.sf_tray_template</field>
|
||||||
|
<field name="binding_model_id" ref="model_sf_tray"/>
|
||||||
|
<field name="binding_type">report</field>
|
||||||
|
<field name="paperformat_id" ref="sf_manufacturing.sf_tray1"/>
|
||||||
|
|
||||||
|
</record>
|
||||||
|
<!-- 托盘码打印模板-->
|
||||||
|
<template id="sf_tray_template">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<div class="page">
|
||||||
|
<div t-field="o.code"
|
||||||
|
t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:350px;height:60px'}"/>
|
||||||
|
<div t-field="o.code" style="text-align: center"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 产品信息打印动作-->
|
||||||
|
<record id="label_sf_tray_code1" model="ir.actions.report">
|
||||||
|
<field name="name">打印产品信息</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">sf_manufacturing.sf_tray_template1</field>
|
||||||
|
<field name="report_file">sf_manufacturing.sf_tray_template1</field>
|
||||||
|
<field name="binding_model_id" ref="model_mrp_workorder"/>
|
||||||
|
<field name="binding_type">report</field>
|
||||||
|
<field name="paperformat_id" ref="sf_manufacturing.sf_tray1"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 产品信息打印模板-->
|
||||||
|
<template id="sf_tray_template1">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<div class="page">
|
||||||
|
<div t-field="o.production_id.name"
|
||||||
|
t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:350px;height:60px'}"/>
|
||||||
|
<div t-field="o.production_id" style="text-align: center"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="name" required="1"/>
|
<field name="name" required="1"/>
|
||||||
<field name="type" readonly="1" string="任务类型"/>
|
<field name="type" readonly="1" string="任务类型"/>
|
||||||
<field name="route_type" string="类型"/>
|
<field name="route_type" string="类型" required="1"/>
|
||||||
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"/>
|
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"/>
|
||||||
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
|
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
|
||||||
<field name="destination_production_line_id" required="1" options="{'no_create': True}"/>
|
<field name="destination_production_line_id" required="1" options="{'no_create': True}"/>
|
||||||
|
|||||||
105
sf_manufacturing/views/mrp_maintenance_views.xml
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="maintenance_equipment_view_form_inherit_mrp" model="ir.ui.view">
|
||||||
|
<field name="name">maintenance.equipment.view.form.inherit.mrp</field>
|
||||||
|
<field name="model">maintenance.equipment</field>
|
||||||
|
<field name="inherit_id" ref="maintenance.hr_equipment_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='button_box']" position="inside">
|
||||||
|
<button name="button_mrp_workcenter" type="object" class="oe_stat_button"
|
||||||
|
icon="fa-cogs" string="Work Center" attrs="{'invisible': [('workcenter_id', '=', False)]}"
|
||||||
|
groups="mrp.group_mrp_routings">
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='location']" position="after">
|
||||||
|
<field name="workcenter_id" context="{'default_company_id':company_id}"
|
||||||
|
groups="mrp.group_mrp_routings"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//group[@name='maintenance']" position="after">
|
||||||
|
<group name="statistics">
|
||||||
|
<label for="expected_mtbf" string="Expected Mean Time Between Failure"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="expected_mtbf"/>
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
<label for="mtbf" string="Mean Time Between Failure"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="mtbf"/>
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
<label for="estimated_next_failure" string="Estimated Next Failure"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="estimated_next_failure"/>
|
||||||
|
</div>
|
||||||
|
<field name="latest_failure_date" string="Latest Failure"/>
|
||||||
|
<label for="mttr" string="Mean Time To Repair"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="mttr"/>
|
||||||
|
days
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="maintenance_request_view_form_inherit_mrp" model="ir.ui.view">
|
||||||
|
<field name="name">maintenance.request.view.form.inherit.mrp</field>
|
||||||
|
<field name="model">maintenance.request</field>
|
||||||
|
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='maintenance_type']" position="after">
|
||||||
|
<field name="production_company_id" invisible="1"/>
|
||||||
|
<field name="workorder_id" invisible="1"/>
|
||||||
|
<field name="production_id" options="{'no_create': True, 'no_open': True}"/>
|
||||||
|
<field name="workorder_id" attrs="{'invisible': [('production_id', '=', False)]}"
|
||||||
|
options="{'no_create': True, 'no_open': True}" domain="[('production_id', '=', production_id)]"
|
||||||
|
groups="mrp.group_mrp_routings"/>
|
||||||
|
<!-- <field name="repair_id"/> -->
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//div[hasclass('oe_chatter')]" position="after">
|
||||||
|
<div invisible="not context.get('discard_on_footer_button', False)">
|
||||||
|
<footer class="oe_edit_only">
|
||||||
|
<button special="save" data-hotkey="v" string="Save" class="oe_highlight"/>
|
||||||
|
<button string="Discard" special="cancel" data-hotkey="z"/>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
<field name="equipment_id" position="attributes">
|
||||||
|
<attribute name="domain">['|', (not workorder_id and 1 or 0, '=', 1), '|', ('workcenter_id', '=',
|
||||||
|
False), ('workcenter_id.order_ids', 'in', workorder_id)]
|
||||||
|
</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="maintenance_request_view_search_inherit_mrp" model="ir.ui.view">
|
||||||
|
<field name="name">maintenence.request.view.search.inherit.mrp</field>
|
||||||
|
<field name="model">maintenance.request</field>
|
||||||
|
<field name="inherit_id" ref="maintenance.hr_equipment_request_view_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='maintenance_team_id']" position="after">
|
||||||
|
<field name="production_id" string="Operation"
|
||||||
|
filter_domain="['|', ('production_id', 'ilike', self), ('workorder_id', 'ilike', self)]"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="maintenance.menu_equipment_form"
|
||||||
|
name="Equipments"
|
||||||
|
parent="maintenance.menu_maintenance_title"
|
||||||
|
groups="maintenance.group_equipment_manager,base.group_user"
|
||||||
|
sequence="2"/>
|
||||||
|
<menuitem id="menu_workcenter_tree"
|
||||||
|
action="mrp.mrp_workcenter_action"
|
||||||
|
groups="mrp.group_mrp_routings"
|
||||||
|
parent="maintenance.menu_equipment_form"
|
||||||
|
sequence="1"/>
|
||||||
|
<menuitem
|
||||||
|
id="menu_equipment_dashboard"
|
||||||
|
name="Machines & Tools"
|
||||||
|
parent="maintenance.menu_equipment_form"
|
||||||
|
action="maintenance.hr_equipment_action"
|
||||||
|
sequence="2"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -623,7 +623,6 @@
|
|||||||
<field name="route_id" options="{'no_create': True}"
|
<field name="route_id" options="{'no_create': True}"
|
||||||
domain="[('route_type','in',['上产线','下产线'])]"/>
|
domain="[('route_type','in',['上产线','下产线'])]"/>
|
||||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
|
||||||
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
||||||
<field name="is_cnc_program_down" readonly="1"/>
|
<field name="is_cnc_program_down" readonly="1"/>
|
||||||
<!-- <field name="rfid_code"/>-->
|
<!-- <field name="rfid_code"/>-->
|
||||||
@@ -673,14 +672,18 @@
|
|||||||
<field name="name">空料架配送</field>
|
<field name="name">空料架配送</field>
|
||||||
<field name="model">sf.workpiece.delivery</field>
|
<field name="model">sf.workpiece.delivery</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="工件配送" class="center" create="0" edit="0" delete="0">
|
<tree string="工件配送" editable="bottom" class="center" delete="0" create="1">
|
||||||
<header>
|
<header>
|
||||||
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
|
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
|
||||||
</header>
|
</header>
|
||||||
<field name="name" string="路线名称" readonly="1"/>
|
<field name="name" string="路线名称" attrs="{'readonly': [('id', '!=', False)]}"
|
||||||
<field name="route_id" options="{'no_create': True}"/>
|
placeholder="例如:运送空料架路线:G01-A01" required="1" force_save="1"/>
|
||||||
<field name="feeder_station_start_id" readonly="1"/>
|
<field name="route_id" options="{'no_create': True}" required="1"
|
||||||
<field name="feeder_station_destination_id" readonly="1"/>
|
attrs="{'readonly': [('id', '!=', False)]}" domain="[('route_type', '=', '运送空料架')]"
|
||||||
|
force_save="1"/>
|
||||||
|
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||||
|
<!-- <field name="type" readonly="1"/>-->
|
||||||
|
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
||||||
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
|
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
|
||||||
string="历史"/>
|
string="历史"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|||||||
31
sf_manufacturing/views/tray_view.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="sf_tray_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">托盘条形码生成</field>
|
||||||
|
<field name="model">sf.tray</field>
|
||||||
|
<field name="inherit_id" ref="sf_base.sf_tray_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//group[@name='group1']" position="after">
|
||||||
|
<notebook>
|
||||||
|
<page string="生成条形码">
|
||||||
|
<field name='qr_image' widget="image"/>
|
||||||
|
<group>
|
||||||
|
<field name='production_id' readonly="1"
|
||||||
|
attrs='{"invisible": [("production_id","=",False)]}'/>
|
||||||
|
<field name="workorder_id"/>
|
||||||
|
</group>
|
||||||
|
<div class="col
|
||||||
|
-12 col-lg-6 o_setting_box">
|
||||||
|
<button type="object" class="oe_highlight" name="unclamp" string="解除装夹"
|
||||||
|
attrs='{"invisible": [("state","=","空闲")]}'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</page>
|
||||||
|
|
||||||
|
</notebook>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -8,33 +8,64 @@ from odoo.http import request
|
|||||||
|
|
||||||
class Manufacturing_Connect(http.Controller):
|
class Manufacturing_Connect(http.Controller):
|
||||||
|
|
||||||
@http.route('/AutoDeviceApi/ToolGroup', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
@http.route('/AutoDeviceApi/ToolInventory', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||||
cors="*")
|
cors="*")
|
||||||
def get_functional_tool_groups_Info(self, **kw):
|
def get_functional_tool_inventory_Info(self, **kw):
|
||||||
"""
|
"""
|
||||||
刀具组接口
|
功能刀具清单接口
|
||||||
:param kw:
|
:param kw:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
logging.info('get_functional_tool_groups_Info:%s' % kw)
|
logging.info('get_functional_tool_inventory_Info:%s' % kw)
|
||||||
try:
|
try:
|
||||||
datas = request.httprequest.data
|
datas = request.httprequest.data
|
||||||
ret = json.loads(datas)
|
ret = json.loads(datas)
|
||||||
# ret = json.loads(ret['result'])
|
# ret = json.loads(ret['result'])
|
||||||
logging.info('DeviceId:%s' % ret)
|
logging.info('DeviceId:%s' % ret)
|
||||||
functional_tools = request.env['sf.tool.inventory'].sudo().search([])
|
tool_inventory = request.env['sf.tool.inventory'].sudo().search([])
|
||||||
|
|
||||||
|
res = {'Succeed': True, 'Datas': []}
|
||||||
|
if tool_inventory:
|
||||||
|
for item in tool_inventory:
|
||||||
|
res['Datas'].append({
|
||||||
|
'ToolName': item.name,
|
||||||
|
'GroupName': item.tool_groups_id.name,
|
||||||
|
'Lifetime': item.life_span
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
|
logging.info('get_functional_tool_inventory_Info error:%s' % e)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
@http.route('/AutoDeviceApi/ToolEntity', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def get_functional_tool_entity_Info(self, **kw):
|
||||||
|
"""
|
||||||
|
功能刀具列表接口
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
logging.info('get_functional_tool_entity_Info:%s' % kw)
|
||||||
|
try:
|
||||||
|
datas = request.httprequest.data
|
||||||
|
ret = json.loads(datas)
|
||||||
|
# ret = json.loads(ret['result'])
|
||||||
|
logging.info('DeviceId:%s' % ret)
|
||||||
|
functional_tools = request.env['sf.functional.cutting.tool.entity'].sudo().search([])
|
||||||
|
|
||||||
res = {'Succeed': True, 'Datas': []}
|
res = {'Succeed': True, 'Datas': []}
|
||||||
if functional_tools:
|
if functional_tools:
|
||||||
for item in functional_tools:
|
for item in functional_tools:
|
||||||
res['Datas'].append({
|
res['Datas'].append({
|
||||||
|
'Rfid': item.rfid,
|
||||||
|
'ToolName': item.tool_name_id.name,
|
||||||
'GroupName': item.tool_groups_id.name,
|
'GroupName': item.tool_groups_id.name,
|
||||||
'ToolId': item.functional_cutting_tool_model_id.name,
|
'MaxLifetime': item.max_lifetime_value,
|
||||||
'ToolName': item.name
|
'KnifeHandle': item.cutting_tool_cutterhandle_model_id.name
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
logging.info('get_functional_tool_groups_Info error:%s' % e)
|
logging.info('get_functional_tool_entity_Info error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
@http.route('/AutoDeviceApi/PutToolParameter', type='http', auth='none', methods=['GET', 'POST'], csrf=False,
|
@http.route('/AutoDeviceApi/PutToolParameter', type='http', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||||
|
|||||||
25
vista_backend_theme/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# Cybrosys Technologies Pvt. Ltd.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||||
|
#
|
||||||
|
# You can modify it under the terms of the GNU LESSER
|
||||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
# (LGPL v3) along with this program.
|
||||||
|
# If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
from .hooks import test_pre_init_hook, test_post_init_hook
|
||||||
|
from . import wizard
|
||||||
|
from . import models
|
||||||
|
|
||||||
70
vista_backend_theme/__manifest__.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# Cybrosys Technologies Pvt. Ltd.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||||
|
#
|
||||||
|
# You can modify it under the terms of the GNU LESSER
|
||||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
# (LGPL v3) along with this program.
|
||||||
|
# If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Vista Backend Theme V16",
|
||||||
|
"description": """Minimalist and elegant backend theme for Odoo 16, Backend Theme, Theme""",
|
||||||
|
"summary": "Vista Backend Theme V16 is an attractive theme for backend",
|
||||||
|
"category": "Themes/Backend",
|
||||||
|
"version": "16.0.1.0.0",
|
||||||
|
'author': 'Cybrosys Techno Solutions',
|
||||||
|
'company': 'Cybrosys Techno Solutions',
|
||||||
|
'maintainer': 'Cybrosys Techno Solutions',
|
||||||
|
'website': "https://www.cybrosys.com",
|
||||||
|
"depends": ['base', 'web', 'mail', 'base_setup'],
|
||||||
|
"data": [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/icons.xml',
|
||||||
|
'views/layout.xml',
|
||||||
|
'views/theme.xml',
|
||||||
|
'views/assets.xml',
|
||||||
|
'data/theme_data.xml',
|
||||||
|
'views/res_config.xml',
|
||||||
|
],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_backend': {
|
||||||
|
'/vista_backend_theme/static/src/scss/theme.scss',
|
||||||
|
'/vista_backend_theme/static/src/js/systray.js',
|
||||||
|
'/vista_backend_theme/static/src/js/load.js',
|
||||||
|
'/vista_backend_theme/static/src/js/chrome/sidebar_menu.js',
|
||||||
|
'/vista_backend_theme/static/src/xml/systray.xml',
|
||||||
|
'/vista_backend_theme/static/src/xml/top_bar.xml',
|
||||||
|
'/vista_backend_theme/static/src/js/web_window_title.js',
|
||||||
|
},
|
||||||
|
'web.assets_frontend': {
|
||||||
|
'/vista_backend_theme/static/src/scss/login.scss',
|
||||||
|
'/vista_backend_theme/static/src/scss/login.scss',
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'images': [
|
||||||
|
'static/description/banner.png',
|
||||||
|
'static/description/theme_screenshot.png',
|
||||||
|
'static/description/main_screenshot.png',
|
||||||
|
],
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'pre_init_hook': 'test_pre_init_hook',
|
||||||
|
'post_init_hook': 'test_post_init_hook',
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
11
vista_backend_theme/data/demo.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record id="config_parameter_web_base_title_demo" model="ir.config_parameter">
|
||||||
|
<field name="key">web.base.title</field>
|
||||||
|
<field name="value">Demo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
||||||
8
vista_backend_theme/data/theme_data.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="theme_data_stored" model="theme.data.stored">
|
||||||
|
<field name="name">default</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
163
vista_backend_theme/hooks.py
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# Cybrosys Technologies Pvt. Ltd.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
||||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
||||||
|
#
|
||||||
|
# You can modify it under the terms of the GNU LESSER
|
||||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
# (LGPL v3) along with this program.
|
||||||
|
# If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
from odoo.modules import get_module_resource
|
||||||
|
|
||||||
|
|
||||||
|
def test_pre_init_hook(cr):
|
||||||
|
"""pre init hook"""
|
||||||
|
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
menu_item = env['ir.ui.menu'].search([('parent_id', '=', False)])
|
||||||
|
|
||||||
|
for menu in menu_item:
|
||||||
|
if menu.name == 'Contacts':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'contacts.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Link Tracker':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'link-tracker.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Dashboards':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'dashboards.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Sales':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'sales.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Invoicing':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'accounting.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Inventory':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'inventory.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Purchase':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'purchase.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Calendar':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'calendar.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'CRM':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'crm.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Note':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'note.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Website':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'website.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Point of Sale':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'pos.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Manufacturing':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'manufacturing.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Repairs':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'repairs.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Email Marketing':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'email-marketing.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'SMS Marketing':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'sms-marketing.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Project':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'project.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Surveys':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'surveys.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Employees':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'employee.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Recruitment':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'recruitment.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Attendances':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'attendances.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Time Off':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'timeoff.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Expenses':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'expenses.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Maintenance':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'maintenance.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Live Chat':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'live-chat.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Lunch':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'lunch.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Fleet':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'fleet.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Timesheets':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'timesheets.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Events':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'events.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'eLearning':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'elearning.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
if menu.name == 'Members':
|
||||||
|
img_path = get_module_resource(
|
||||||
|
'vista_backend_theme', 'static', 'src', 'img', 'icons', 'members.png')
|
||||||
|
menu.write({'web_icon_data': base64.b64encode(open(img_path, "rb").read())})
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_init_hook(cr, registry):
|
||||||
|
"""post init hook"""
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
4
vista_backend_theme/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import ir_ui_view
|
||||||
|
from . import res_config
|
||||||
18
vista_backend_theme/models/ir_ui_view.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class View(models.Model):
|
||||||
|
_inherit = 'ir.ui.view'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _render_template(self, template, values=None):
|
||||||
|
if template in ['web.login', 'web.webclient_bootstrap']:
|
||||||
|
if not values:
|
||||||
|
values = {}
|
||||||
|
values["title"] = self.env['ir.config_parameter'].sudo().get_param("web.base.title", "")
|
||||||
|
return super(View, self)._render_template(template, values)
|
||||||
29
vista_backend_theme/models/res_config.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONFIG_PARAM_WEB_WINDOW_TITLE = "web.base.title"
|
||||||
|
|
||||||
|
class ResConfigSettings(models.TransientModel):
|
||||||
|
_inherit = 'res.config.settings'
|
||||||
|
|
||||||
|
web_window_title = fields.Char('Window Title')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_values(self):
|
||||||
|
res = super(ResConfigSettings, self).get_values()
|
||||||
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
|
web_window_title = ir_config.get_param(CONFIG_PARAM_WEB_WINDOW_TITLE, default='')
|
||||||
|
res.update(
|
||||||
|
web_window_title=web_window_title
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def set_values(self):
|
||||||
|
super(ResConfigSettings, self).set_values()
|
||||||
|
ir_config = self.env['ir.config_parameter'].sudo()
|
||||||
|
ir_config.set_param(CONFIG_PARAM_WEB_WINDOW_TITLE, self.web_window_title or "")
|
||||||
2
vista_backend_theme/security/ir.model.access.csv
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_theme_data,access.theme.data,model_theme_data,,1,1,1,1
|
||||||
|
BIN
vista_backend_theme/static/description/assets/icons/chevron.png
Normal file
|
After Width: | Height: | Size: 310 B |
BIN
vista_backend_theme/static/description/assets/icons/cogs.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 576 B |
|
After Width: | Height: | Size: 733 B |
|
After Width: | Height: | Size: 911 B |
BIN
vista_backend_theme/static/description/assets/icons/license.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
vista_backend_theme/static/description/assets/icons/lifebuoy.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 878 B |
BIN
vista_backend_theme/static/description/assets/icons/puzzle.png
Normal file
|
After Width: | Height: | Size: 653 B |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 839 B |
|
After Width: | Height: | Size: 427 B |
BIN
vista_backend_theme/static/description/assets/icons/training.png
Normal file
|
After Width: | Height: | Size: 627 B |
BIN
vista_backend_theme/static/description/assets/icons/update.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
vista_backend_theme/static/description/assets/icons/user.png
Normal file
|
After Width: | Height: | Size: 988 B |
BIN
vista_backend_theme/static/description/assets/icons/wrench.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
vista_backend_theme/static/description/banner.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
vista_backend_theme/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
vista_backend_theme/static/description/images/app_drawer.png
Normal file
|
After Width: | Height: | Size: 333 KiB |
BIN
vista_backend_theme/static/description/images/custom_date.png
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
vista_backend_theme/static/description/images/discuss.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
vista_backend_theme/static/description/images/discuss_mobile.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
vista_backend_theme/static/description/images/form_view.png
Normal file
|
After Width: | Height: | Size: 270 KiB |
BIN
vista_backend_theme/static/description/images/hero.gif
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
vista_backend_theme/static/description/images/icons/design.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
vista_backend_theme/static/description/images/icons/quality.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
BIN
vista_backend_theme/static/description/images/kanban.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
vista_backend_theme/static/description/images/kanban_mobile.png
Normal file
|
After Width: | Height: | Size: 886 KiB |
BIN
vista_backend_theme/static/description/images/login.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
vista_backend_theme/static/description/images/responsive.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
vista_backend_theme/static/description/images/sale_tree_view.png
Normal file
|
After Width: | Height: | Size: 334 KiB |
BIN
vista_backend_theme/static/description/images/search.png
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
vista_backend_theme/static/description/images/tree_view.png
Normal file
|
After Width: | Height: | Size: 350 KiB |
444
vista_backend_theme/static/description/index.html
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
<!-- HERO SECTION -->
|
||||||
|
<div class="contianer">
|
||||||
|
<div class="row position-relative"
|
||||||
|
style="background-color: #2f3542 !important; height: 400px; margin-bottom: 6rem; border-radius: 1rem !important;">
|
||||||
|
<div class="col-lg-12 d-flex flex-column justify-content-start align-items-center">
|
||||||
|
<h1 class="display-1 text-white" style="padding-top: 5rem;">Vista Backend Theme V16</h1>
|
||||||
|
<p class="text-light small font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase;">Multi-Color
|
||||||
|
& Multi-Design Backend Theme for
|
||||||
|
Odoo 16</p>
|
||||||
|
</div>
|
||||||
|
<img src="./images/hero.gif" class="img img-fluid"
|
||||||
|
style="height: auto; width: 525px; top: 45%; left: 0; right: 0; margin-left: auto; margin-right: auto;"
|
||||||
|
height="auto" width="525px">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF HERO SECTION -->
|
||||||
|
<!-- SHORT DESCRIPTION -->
|
||||||
|
<div class="container">
|
||||||
|
<div class="row my-4">
|
||||||
|
<div class="col-lg-12 d-flex justify-content-center align-items-center">
|
||||||
|
<h6 class="text-muted text-center w-50" style="line-height: 22px;">The app enables a user friendly backend
|
||||||
|
theme for Odoo 16.0 community edition.</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF SHORT DESCRIPTION -->
|
||||||
|
<!-- FEATURE ICONS -->
|
||||||
|
<div class="container w-50" style="margin: 3rem auto;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-4 my-2 d-flex flex-column justify-content-center align-items-center">
|
||||||
|
<div
|
||||||
|
style="height: 100px; width: 100px; border: 8px solid #eaebec; border-radius: 50%; background-color: #e0e1e3; box-shadow: 0px 0px 0px 8px #f3f3f4;"
|
||||||
|
class="d-flex justify-content-center align-items-center">
|
||||||
|
<img height="60px" src="./images/icons/design.png">
|
||||||
|
</div>
|
||||||
|
<h6 class="my-4 text-center">Carefully Crafted</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 my-2 d-flex flex-column justify-content-center align-items-center">
|
||||||
|
<div
|
||||||
|
style="height: 100px; width: 100px; border: 8px solid #eaebec; border-radius: 50%; background-color: #e0e1e3; box-shadow: 0px 0px 0px 8px #f3f3f4;"
|
||||||
|
class="d-flex justify-content-center align-items-center">
|
||||||
|
<img height="60px" src="./images/icons/responsive.png">
|
||||||
|
</div>
|
||||||
|
<h6 class="my-4 text-center">Responsive Design</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 my-2 d-flex flex-column justify-content-center align-items-center">
|
||||||
|
<div
|
||||||
|
style="height: 100px; width: 100px; border: 8px solid #eaebec; border-radius: 50%; background-color: #e0e1e3; box-shadow: 0px 0px 0px 8px #f3f3f4;"
|
||||||
|
class="d-flex justify-content-center align-items-center">
|
||||||
|
<img height="60px" src="./images/icons/quality.png">
|
||||||
|
</div>
|
||||||
|
<h6 class="my-4 text-center">Quality Checked</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF FEATURE ICONS -->
|
||||||
|
|
||||||
|
<!-- ONE COLUMN SECTION-->
|
||||||
|
<div class="container" style="margin: 3rem auto;">
|
||||||
|
<div class="row my-4">
|
||||||
|
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
|
||||||
|
<h2 class="text-center mt-3 display-4 text-weight-bold">Kanban View</h2>
|
||||||
|
<p class="text-center lead text-muted mb-4">Kanban view with a clean layout and modified font.</p>
|
||||||
|
<img height="600px" width="auto" src="./images/kanban.png" class="img img-fluid deep-4 rounded">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF ONE COLUMN SECTION-->
|
||||||
|
|
||||||
|
<!-- TWO COLUMN SECTION-->
|
||||||
|
<div class="container" style="margin: 6rem auto;">
|
||||||
|
<div class="row my-4">
|
||||||
|
<div class="col-lg-6 d-flex flex-column justify-content-center align-items-start">
|
||||||
|
<span class="font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase; color: #444955">Custom
|
||||||
|
Login</span>
|
||||||
|
<h2 class="mt-3">Minimal, Colorful Login Screen</h2>
|
||||||
|
<p class="lead text-muted mb-4">Customized minimal and colorful login screen.</p>
|
||||||
|
<img src="./images/login.png" class="img img-fluid deep-2 rounded">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6 d-flex flex-column justify-content-center align-items-start">
|
||||||
|
<span class="font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase; color: #444955">Colored UI
|
||||||
|
Elements</span>
|
||||||
|
<h2 class="mt-3">Discuss</h2>
|
||||||
|
<p class="lead text-muted mb-4">Discuss page with a different style.</p>
|
||||||
|
<img src="./images/discuss.png" class="img img-fluid deep-2 rounded">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF TWO COLUMN SECTION-->
|
||||||
|
|
||||||
|
<!-- RESPONSIVE SECTION-->
|
||||||
|
<div class="container" style="margin: 6rem auto;">
|
||||||
|
<div class="row my-4">
|
||||||
|
<div class="col-lg-5 d-flex flex-column justify-content-center align-items-start">
|
||||||
|
<img src="./images/responsive.png" class="img img-fluid deep-2 rounded">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-7 d-flex flex-column justify-content-center">
|
||||||
|
<span class="font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase; color: #444955">Responsive
|
||||||
|
Layout</span>
|
||||||
|
<h2 class="mt-3">Truly Responsive</h2>
|
||||||
|
<p class="lead text-muted mb-4">Fully responsive layout which enables to view and manage everything from the
|
||||||
|
comfort of your mobile device.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- END OF RESPONSIVE SECTION-->
|
||||||
|
|
||||||
|
<!-- RESPONSIVE SECTION-->
|
||||||
|
<div class="container" style="margin: 6rem auto;">
|
||||||
|
<div class="row my-4">
|
||||||
|
<div class="col-lg-7 d-flex flex-column justify-content-center">
|
||||||
|
<span class="font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase; color: #444955">Responsive
|
||||||
|
Layout</span>
|
||||||
|
<h2 class="mt-3">Modified App Drawer</h2>
|
||||||
|
<p class="lead text-muted mb-4">Modified app drawer which helps to navigate through different applications.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-5 d-flex flex-column justify-content-center align-items-start">
|
||||||
|
<img src="./images/app_drawer.png" class="img img-fluid deep-2 rounded">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF RESPONSIVE SECTION-->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- RESPONSIVE SECTION-->
|
||||||
|
<div class="container" style="margin: 6rem auto;">
|
||||||
|
<div class="row my-4">
|
||||||
|
<div class="col-lg-5 d-flex flex-column justify-content-center align-items-start">
|
||||||
|
<img src="./images/custom_date.png" class="img img-fluid deep-2 rounded">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-7 d-flex flex-column justify-content-center">
|
||||||
|
<span class="font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase; color: #444955">Colored UI
|
||||||
|
Elements</span>
|
||||||
|
<h2 class="mt-3">Custom Date Picker</h2>
|
||||||
|
<p class="lead text-muted mb-4">Customized date picker</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF RESPONSIVE SECTION-->
|
||||||
|
|
||||||
|
<!-- TWO COLUMN SECTION-->
|
||||||
|
<div class="container" style="margin: 6rem auto;">
|
||||||
|
<div class="row my-4">
|
||||||
|
<div class="col-lg-6 d-flex flex-column justify-content-center align-items-start">
|
||||||
|
<span class="font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase; color: #444955">Colored UI
|
||||||
|
Elements</span>
|
||||||
|
<h2 class="mt-3">Tree View</h2>
|
||||||
|
<p class="lead text-muted mb-4">Tree view with a clean layout and modified font.</p>
|
||||||
|
<img src="./images/tree_view.png" class="img img-fluid deep-2 rounded">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6 d-flex flex-column justify-content-center align-items-start">
|
||||||
|
<span class="font-weight-bold" style="letter-spacing: 2px; text-transform: uppercase; color: #444955">Colored UI
|
||||||
|
Elements</span>
|
||||||
|
<h2 class="mt-3">Form View</h2>
|
||||||
|
<p class="lead text-muted mb-4">Form view with a clean layout and modified font.</p>
|
||||||
|
<img src="./images/form_view.png" class="img img-fluid deep-2 rounded">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END OF TWO COLUMN SECTION-->
|
||||||
|
|
||||||
|
<!-- OUR SERVICES -->
|
||||||
|
<section class="container" style="margin-top: 6rem !important;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
|
||||||
|
<h2 class="text-center"
|
||||||
|
style="font-family: Montserrat, 'sans-serif'; color: #000 !important; font-weight: 800 !important; font-size: 2rem !important; width: 80%;">
|
||||||
|
Our Services</h2>
|
||||||
|
<p class="text-center"
|
||||||
|
style="font-family: Montserrat, 'sans-serif'; color: #1a1a1a !important; font-weight: 300 !important; font-size: 1.3rem !important;">
|
||||||
|
We provide following services</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/cogs.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Customization</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/wrench.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Implementation</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/lifebuoy.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Support</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/user.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Hire
|
||||||
|
Odoo
|
||||||
|
Developer</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/puzzle.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Integration</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/update.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Migration</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/consultation.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Consultancy</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/training.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Implementation</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
|
||||||
|
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
|
||||||
|
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;">
|
||||||
|
<img src="assets/icons/license.png" class="img-responsive" height="48px" width="48px">
|
||||||
|
</div>
|
||||||
|
<h6 class="text-center" style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">Odoo
|
||||||
|
Licensing Consultancy</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- END OF END OF OUR SERVICES -->
|
||||||
|
|
||||||
|
<!-- OUR INDUSTRIES -->
|
||||||
|
<section class="container" style="margin-top: 6rem !important;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
|
||||||
|
<h2 class="text-center"
|
||||||
|
style="font-family: Montserrat, 'sans-serif'; color: #000 !important; font-weight: 800 !important; font-size: 2rem !important; width: 80%;">
|
||||||
|
Our Industries</h2>
|
||||||
|
<p class="text-center"
|
||||||
|
style="font-family: Montserrat, 'sans-serif'; color: #1a1a1a !important; font-weight: 300 !important; font-size: 1.3rem !important;">
|
||||||
|
Our industry specifics and process segments to solve your complex business barriers.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/trading-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
Trading
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">Easily procure
|
||||||
|
and
|
||||||
|
sell your products</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/pos-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
POS
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">Easy
|
||||||
|
configuration
|
||||||
|
and convivial experience</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/education-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
Education
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">A platform for
|
||||||
|
educational management</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/manufacturing-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
Manufacturing
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">Plan, track and
|
||||||
|
schedule your operations</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/ecom-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
E-commerce & Website
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">Mobile
|
||||||
|
friendly,
|
||||||
|
awe-inspiring product pages</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/service-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
Service Management
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">Keep track of
|
||||||
|
services and invoice</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/restaurant-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
Restaurant
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">Run your bar or
|
||||||
|
restaurant methodically</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3">
|
||||||
|
<div class="my-4 d-flex flex-column justify-content-center"
|
||||||
|
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
|
||||||
|
<img src="./assets/icons/hotel-black.png" class="img-responsive mb-3" height="48px" width="48px">
|
||||||
|
<h5 style="color: #000 !important; font-weight: bold;">
|
||||||
|
Hotel Management
|
||||||
|
</h5>
|
||||||
|
<p style="font-size: 0.9rem !important;">An
|
||||||
|
all-inclusive
|
||||||
|
hotel management application</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- END OF END OF OUR INDUSTRIES -->
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<!-- Footer Section -->
|
||||||
|
<section class="container" style="margin: 5rem auto 2rem;">
|
||||||
|
<div class="row" style="max-width:1540px;">
|
||||||
|
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
|
||||||
|
<h2 class="text-center"
|
||||||
|
style="color: #000 !important; font-weight: 800 !important; font-size: 2rem !important; width: 80%;">
|
||||||
|
Need Help?</h2>
|
||||||
|
<p class="text-center"
|
||||||
|
style="color: #1a1a1a !important; font-weight: 300 !important; font-size: 1.3rem !important;">
|
||||||
|
Do you have any queries regarding our products & services? Let us know.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contact Cards -->
|
||||||
|
<div class="row d-flex justify-content-center align-items-center" style="max-width:1540px; margin: 0 auto 2rem auto;">
|
||||||
|
|
||||||
|
<div class="col-lg-12" style="padding: 0rem 3rem 2rem; border-radius: 10px; margin-right: 3rem; ">
|
||||||
|
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<a href="mailto:odoo@cybrosys.com" target="_blank" class="btn btn-block mb-2 deep_hover"
|
||||||
|
style="text-decoration: none; background-color: #4d4d4d; color: #FFF; border-radius: 4px;"><i
|
||||||
|
class="fa fa-envelope mr-2"></i>odoo@cybrosys.com</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<a href="https://api.whatsapp.com/send?phone=918606827707" target="_blank"
|
||||||
|
class="btn btn-block mb-2 deep_hover"
|
||||||
|
style="text-decoration: none; background-color: #25D366; color: #FFF; border-radius: 4px;"><i
|
||||||
|
class="fa fa-whatsapp mr-2"></i>WhatsApp</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<a href="skype:cybrosystechnologies?chat" target="_blank" class="btn btn-block deep_hover"
|
||||||
|
style="text-decoration: none; background-color: #4d4d4d; color: #FFF; border-radius: 4px;"><i
|
||||||
|
class="fa fa-envelope mr-2"></i>cybrosystechnologies</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- End of Contact Cards -->
|
||||||
|
</section>
|
||||||
|
<!-- Footer -->
|
||||||
|
<section class="oe_container" style="padding: 2rem 3rem 1rem;">
|
||||||
|
<div class="row" style="max-width:1540px; margin: 0 auto; margin-right: 3rem; ">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="col-lg-12 d-flex justify-content-center align-items-center" style="margin-top: 3rem;">
|
||||||
|
<img src="https://www.cybrosys.com/images/logo.png" width="200px" height="auto" />
|
||||||
|
</div>
|
||||||
|
<!-- End of Logo -->
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<hr
|
||||||
|
style="margin-top: 3rem;background: linear-gradient(90deg, rgba(2,0,36,0) 0%, rgba(229,229,229,1) 33%, rgba(229,229,229,1) 58%, rgba(0,212,255,0) 100%); height: 2px; border-style: none;">
|
||||||
|
<!-- End of Footer Section -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- END OF FOOTER -->
|
||||||
BIN
vista_backend_theme/static/description/main_screenshot.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
vista_backend_theme/static/description/theme_screenshot.png
Normal file
|
After Width: | Height: | Size: 311 KiB |
BIN
vista_backend_theme/static/src/img/icons/accounting.png
Normal file
|
After Width: | Height: | Size: 899 B |
BIN
vista_backend_theme/static/src/img/icons/apps.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
vista_backend_theme/static/src/img/icons/attendance.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
vista_backend_theme/static/src/img/icons/bell.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
vista_backend_theme/static/src/img/icons/calendar.png
Normal file
|
After Width: | Height: | Size: 527 B |
BIN
vista_backend_theme/static/src/img/icons/contacts.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
vista_backend_theme/static/src/img/icons/crm.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
vista_backend_theme/static/src/img/icons/dashboards.png
Normal file
|
After Width: | Height: | Size: 523 B |
BIN
vista_backend_theme/static/src/img/icons/discuss.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
vista_backend_theme/static/src/img/icons/documents.png
Normal file
|
After Width: | Height: | Size: 694 B |
BIN
vista_backend_theme/static/src/img/icons/dots-menu-green.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
vista_backend_theme/static/src/img/icons/dots-menu-navy.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
vista_backend_theme/static/src/img/icons/dots-menu-primary.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
vista_backend_theme/static/src/img/icons/dots-menu.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
vista_backend_theme/static/src/img/icons/dots-menu2.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
vista_backend_theme/static/src/img/icons/elearning.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
vista_backend_theme/static/src/img/icons/email-marketing.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
vista_backend_theme/static/src/img/icons/employee.png
Normal file
|
After Width: | Height: | Size: 854 B |
BIN
vista_backend_theme/static/src/img/icons/events.png
Normal file
|
After Width: | Height: | Size: 649 B |
BIN
vista_backend_theme/static/src/img/icons/expenses.png
Normal file
|
After Width: | Height: | Size: 975 B |
BIN
vista_backend_theme/static/src/img/icons/fleet.png
Normal file
|
After Width: | Height: | Size: 707 B |
BIN
vista_backend_theme/static/src/img/icons/inventory.png
Normal file
|
After Width: | Height: | Size: 809 B |
BIN
vista_backend_theme/static/src/img/icons/link-tracker.png
Normal file
|
After Width: | Height: | Size: 792 B |
BIN
vista_backend_theme/static/src/img/icons/live-chat.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
vista_backend_theme/static/src/img/icons/lunch.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
vista_backend_theme/static/src/img/icons/maintenance.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
vista_backend_theme/static/src/img/icons/manufacturing.png
Normal file
|
After Width: | Height: | Size: 788 B |
BIN
vista_backend_theme/static/src/img/icons/members.png
Normal file
|
After Width: | Height: | Size: 863 B |
BIN
vista_backend_theme/static/src/img/icons/notes.png
Normal file
|
After Width: | Height: | Size: 458 B |
BIN
vista_backend_theme/static/src/img/icons/planning.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
vista_backend_theme/static/src/img/icons/pos.png
Normal file
|
After Width: | Height: | Size: 776 B |
BIN
vista_backend_theme/static/src/img/icons/project.png
Normal file
|
After Width: | Height: | Size: 933 B |
BIN
vista_backend_theme/static/src/img/icons/purchase.png
Normal file
|
After Width: | Height: | Size: 390 B |
BIN
vista_backend_theme/static/src/img/icons/recruitment.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |