Merge branch 'refs/heads/feature/程序用刀异常提醒' into feature/销售和排程添加消息推送

# Conflicts:
#	sf_message/models/sf_message_workorder.py
This commit is contained in:
guanhuan
2024-09-26 13:54:28 +08:00
183 changed files with 343 additions and 5771 deletions

View File

@@ -16,7 +16,7 @@
<!-- hide 登录页面 powerd by odoo 及管理数据库 -->
<template id="login_page_layout" inherit_id="web.login_layout" name="Login Page Layout">
<xpath expr="//div[@class='card-body']//div[last()]" position="replace"></xpath>
<xpath expr="//div[@class='card-body']/div[last()]" position="replace"></xpath>
</template>
<!-- 隐藏odoo版本信息 -->

View File

@@ -1 +0,0 @@
from . import models

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': '机企猫智能工厂 消息提醒',
'version': '1.0',
'summary': '智能工厂消息提醒模块',
'sequence': 1,
'description': """
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['jikimo_message_notify'],
'data': [
'security/ir.model.access.csv',
'data/bussiness_node.xml',
# 'views/sf_message_template_view.xml',
],
'test': [
],
'license': 'LGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" ?>
<odoo>
<data>
<record id="bussiness_1" model="jikimo.message.bussiness.node">
<field name="name">订单确认</field>
<field name="model">sale.order</field>
</record>
</data>
</odoo>

View File

@@ -1,2 +0,0 @@
from . import jikimo_message_template
from . import sale_order

View File

@@ -1,10 +0,0 @@
from odoo import models, fields, api
class JikimoMessageTemplate(models.Model):
_inherit = "jikimo.message.template"
def _get_message_model(self):
res = super(JikimoMessageTemplate, self)._get_message_model()
res.append("sale.order")
return res

View File

@@ -1,12 +0,0 @@
from odoo import models, fields, api
class SaleOrder(models.Model):
_name = "sale.order"
_description = "销售订单"
_inherit = ["sale.order", "jikimo.message.dispatch"]
def create(self, vals_list):
res = super(SaleOrder, self).create(vals_list)
res.add_queue('订单确认')
return res

View File

@@ -1,5 +0,0 @@
<odoo>
<data>
</data>
</odoo>

View File

@@ -1,6 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink

View File

@@ -1,76 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © <2016> <top hy>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<data>
<record id="sf_message_template_view_form" model="ir.ui.view">
<field name="name">sf.message.template.view.form</field>
<field name="model">message.template</field>
<field name="arch" type="xml">
<form string="消息模板">
<sheet>
<div class="oe_title">
<label for="name"/>
<h1>
<field name="name" class="w-100" required="1"/>
</h1>
</div>
<group>
<!-- <field name="type"/>-->
<field name="notify_model_id"/>
<field name="content" widget="html" class="oe-bordered-editor"
options="{'style-inline': true, 'codeview': true, 'dynamic_placeholder': true}"/>
<field name="description"/>
<field name="msgtype"/>
<field name="notification_department_id"/>
<field name="notification_employee_ids" widget="many2many_tags"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="sf_message_template_view_tree" model="ir.ui.view">
<field name="name">sf.message.template.view.tree</field>
<field name="model">message.template</field>
<field name="arch" type="xml">
<tree string="消息模板">
<field name="name"/>
<!-- <field name="type"/>-->
<field name="content"/>
<field name="msgtype"/>
<field name="notification_department_id"/>
<field name="notification_employee_ids" widget="many2many_tags"/>
<field name="description"/>
</tree>
</field>
</record>
<record id="sf_message_template_search_view" model="ir.ui.view">
<field name="name">sf.message.template.search.view</field>
<field name="model">message.template</field>
<field name="arch" type="xml">
<search>
<field name="name" string="模糊搜索"
filter_domain="['|','|',('name','like',self),('description','like',self)]"/>
<field name="name"/>
<filter name="filter_active" string="已归档" domain="[('active','=',False)]"/>
</search>
</field>
</record>
<!--定义单证类型视图动作-->
<record id="sf_message_template_action" model="ir.actions.act_window">
<field name="name">消息模板</field>
<field name="res_model">message.template</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="sf_message_template_view_tree"/>
</record>
<menuitem id="msg_set_menu" name="消息设置" parent="base.menu_administration" sequence="1"/>
<menuitem id="sf_message_template_send_menu" name="消息模板" parent="msg_set_menu"
action="sf_message_template_action" sequence="1"/>
</data>
</odoo>

View File

@@ -159,9 +159,6 @@ td.o_required_modifier {
display:inline;
}
.diameter{
display: flex !important;
justify-content: flex-start !important;
align-items: center !important;
}
.o_address_format {
display: flex !important;

View File

@@ -578,7 +578,7 @@
</field>
</record>
<record model="ir.ui.view" id="view_cutting_tool_material_search">
<record model="ir.ui.view" id="view_cutting_tool_inventory_search">
<field name="name">sf.tool.inventory.search</field>
<field name="model">sf.tool.inventory</field>
<field name="arch" type="xml">

View File

@@ -7,10 +7,11 @@
'sequence': 1,
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['hr'],
'depends': ['base', 'hr'],
'data': [
'views/hr_employee.xml',
'views/res_config_settings_views.xml',
'views/res_users_view.xml',
'data/cron_data.xml',
],
'demo': [

View File

@@ -2,3 +2,4 @@
from . import hr_employee
from . import res_config_setting
from . import res_users

View File

@@ -20,7 +20,9 @@ class JkmPracticeEmployee(models.Model):
if result['employee_list']:
for employee_info in result['employee_list']:
if employee_info['work_email']:
self.sudo().search([('work_email', '=', employee_info['work_email'])]).write(
{'we_id': employee_info['we_id']})
hr_employee = self.sudo().search([('work_email', '=', employee_info['work_email'])])
hr_employee.write({'we_id': employee_info['we_id']})
if hr_employee.user_id:
hr_employee.user_id.write({'we_employee_id': employee_info['we_id']})
else:
logging.info('_employee_info_sync error:%s' % result['message'])

12
sf_hr/models/res_users.py Normal file
View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
import logging
_logger = logging.getLogger(__name__)
class ResUsers(models.Model):
_inherit = 'res.users'
we_employee_id = fields.Char(string=u'企业微信账号', default="")

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_users_account_form" model="ir.ui.view">
<field name="name">res.users.account.form</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<page name="preferences" position="after">
<page name="account" string="企业微信">
<group>
<field name="we_employee_id"/>
</group>
</page>
</page>
</field>
</record>
</data>
</odoo>

View File

@@ -231,6 +231,95 @@ class Sf_Dashboard_Connect(http.Controller):
res['message'] = '前端请求日志数据失败,原因:%s' % e
return json.dumps(res)
@http.route('/api/logs/page_data', type='http', auth='public', methods=['GET', 'POST'],
csrf=False, cors="*")
def logs_page_data(self, **kw):
"""
拿到日志数据返回给大屏展示(支持时间戳分页)
:param kw:
:return:
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求日志数据的参数为:%s' % kw)
try:
# 连接数据库
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
# 获取并解析传递的参数
machine_list = ast.literal_eval(kw.get('machine_list', '[]'))
begin_time_str = kw.get('begin_time', '').strip('"')
end_time_str = kw.get('end_time', '').strip('"')
page = int(kw.get('page', 1)) # 默认页码为1
page_size = int(kw.get('page_size', 80)) # 默认每页条数为10
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# 计算分页的 offset
offset = (page - 1) * page_size
# 先查询符合条件的总记录数
total_records = 0
for item in machine_list:
count_sql = '''
SELECT COUNT(*)
FROM device_data
WHERE device_name = %s AND time >= %s AND time <= %s;
'''
# 执行总记录数查询
cur.execute(count_sql, (item, begin_time, end_time))
record_count = cur.fetchone()[0] # 获取总记录数
total_records += record_count
# 计算总页数
if total_records > 0:
total_pages = (total_records + page_size - 1) // page_size # 向上取整
else:
total_pages = 0
# 将总页数和总记录数返回到响应中
res['total_records'] = total_records
res['total_pages'] = total_pages
for item in machine_list:
sql = '''
SELECT time, device_state, program_name
FROM device_data
WHERE device_name = %s AND time >= %s AND time <= %s
ORDER BY time DESC
LIMIT %s OFFSET %s;
'''
# 执行SQL命令使用参数绑定
cur.execute(sql, (item, begin_time, end_time, page_size, offset))
results = cur.fetchall()
# 将数据按照 equipment_code 进行分组
if item not in res['data']:
res['data'][item] = []
for result in results:
res['data'][item].append({
'time': result[0].strftime('%Y-%m-%d %H:%M:%S'),
'state': result[1],
'production_name': result[2],
})
return json.dumps(res) # 返回分页数据
except Exception as e:
logging.info('前端请求日志数据失败,原因:%s' % e)
res['status'] = -1
res['message'] = '前端请求日志数据失败,原因:%s' % e
return json.dumps(res)
finally:
if cur:
cur.close()
if conn:
conn.close()
# 返回CNC机床列表
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
@@ -378,7 +467,8 @@ class Sf_Dashboard_Connect(http.Controller):
pass_rate = 1
if pass_nums:
pass_rate = round(
(len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3)
# (len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3)
(len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3)
# 返工率
rework_rate = round(
@@ -418,8 +508,9 @@ class Sf_Dashboard_Connect(http.Controller):
'plan_data_progress_deviation': plan_data_progress_deviation,
'plan_data_rework_counts': plan_data_rework_counts,
'on_time_rate': on_time_rate,
'detection_data': detection_data,
'pass_rate': pass_rate
# 'detection_data': detection_data,
'detection_data': plan_data_finish_counts,
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts
}
res['data'][line] = data
@@ -489,11 +580,14 @@ class Sf_Dashboard_Connect(http.Controller):
for time_interval in time_intervals:
start_time, end_time = time_interval
# print(start_time, end_time)
orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']),
(date_field_name, '>=', start_time.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', end_time.strftime('%Y-%m-%d 00:00:00'))
])
orders = plan_obj.search([
('production_line_id.name', '=', line),
('state', 'in', ['finished']),
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
])
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
time_count_dict[key] = len(orders)
@@ -715,7 +809,8 @@ class Sf_Dashboard_Connect(http.Controller):
'material': material,
'dimensions': dimensions,
'order_qty': finish_order.product_qty,
'finish_time': finish_order.actual_end_time.strftime('%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' '
'finish_time': finish_order.actual_end_time.strftime(
'%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' '
}
done_data.append(line_dict)
@@ -799,54 +894,27 @@ class Sf_Dashboard_Connect(http.Controller):
"""
查询设备的异常情况
"""
res = {'status': 1, 'message': '成功', 'data': {}}
res = {'status': 1, 'message': '成功', 'data': []}
logging.info('前端请求机床数据的参数为:%s' % kw)
# 连接数据库
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
try:
# 获取请求的机床数据
maintenance_logs_obj = request.env['sf.maintenance.logs'].sudo()
# # 获取请求的机床数据
# machine_list = ast.literal_eval(kw['machine_list'])
# idle_times = []
# idle_dict = {}
# for item in machine_list:
sql = '''
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_message, system_date, system_time, alarm_repair_time
FROM device_data
WHERE alarm_time IS NOT NULL
ORDER BY alarm_time, time;
# machine_data = equipment_obj.search([('code', '=', item)])
for log in maintenance_logs_obj.search([]):
res['data'].append({
'name': log.name,
'alarm_time': log.alarm_time.strftime('%Y-%m-%d %H:%M:%S'),
'fault_alarm_info': log.fault_alarm_info if log.fault_alarm_info else ' ',
'fault_process': log.fault_process if log.fault_process else ' ',
})
'''
# 执行SQL命令
cur.execute(sql)
result = cur.fetchall()
# print('result', result)
# 将查询结果转换为字典列表
data = []
for row in result:
record = {
'alarm_time': row[0],
'alarm_message': row[1],
'system_date': row[2],
'system_time': row[3],
'alarm_repair_time': row[4]
}
data.append(record)
# 将数据填充到返回结果中
res['data'] = data
# 返回统计结果
return json.dumps(res, ensure_ascii=False)
except Exception as e:
print(f"An error occurred: {e}")
return json.dumps(res)
finally:
cur.close()
conn.close()
logging.error(f"An error occurred: {e}")
return json.dumps(res)
# 设备oee
@http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
@@ -1163,6 +1231,7 @@ class Sf_Dashboard_Connect(http.Controller):
conn = psycopg2.connect(**db_config)
# 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list'])
time_threshold = datetime.now() - timedelta(days=1)
def fetch_result_as_dict(cursor):
"""辅助函数:将查询结果转为字典"""
@@ -1172,18 +1241,29 @@ class Sf_Dashboard_Connect(http.Controller):
for item in machine_list:
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND device_state != '离线'
ORDER BY time DESC
LIMIT 1;
""", (item,))
SELECT * FROM device_data
WHERE device_name = %s
AND device_state != '离线'
ORDER BY time DESC
LIMIT 1;
""", (item,))
last_all_time = fetch_result_as_dict(cur)
with conn.cursor() as cur:
cur.execute("""
SELECT * FROM device_data
WHERE device_name = %s
AND device_state != '离线' AND time >= %s
ORDER BY time ASC
LIMIT 1;
""", (item, time_threshold))
last_24_time = fetch_result_as_dict(cur)
# 返回数据
res['data'][item] = {
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0,
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0
'cut_24_time': last_24_time['process_time'] if last_24_time['process_time'] is not None else 0,
'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0,
'power_on_24_time': last_24_time['power_on_time'] if last_24_time['power_on_time'] is not None else 0
}
conn.close()

View File

@@ -1,13 +1,14 @@
# -*-coding:utf-8-*-
from odoo import fields, models
from odoo import fields, models, api
class SfMaintenanceLogs(models.Model):
_name = 'sf.maintenance.logs'
_description = '设备故障日志'
_order = 'alarm_time desc'
code = fields.Char(string='编码')
name = fields.Char(string='名称')
code = fields.Char(string='编码', readonly=True)
name = fields.Char(string='名称', compute='_compute_name')
type = fields.Selection([('type1', '类型1'), ('type2', '类型2')], string='类型')
brand = fields.Many2one('sf.machine.brand', related='maintenance_equipment_id.brand_id', string='品牌')
maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='机台号')
@@ -28,3 +29,13 @@ class SfMaintenanceLogs(models.Model):
fault_duration = fields.Float(string='故障时长')
note = fields.Text(string='备注')
active = fields.Boolean('Active', default=True)
@api.depends('code')
def _compute_name(self):
for record in self:
if record.code:
record.name = self.env['maintenance.equipment'].sudo().search([('code', '=', record.code), ('active', '=', True)]).name
record.maintenance_equipment_id = self.env['maintenance.equipment'].sudo().search([('code', '=', record.code), ('active', '=', True)]).id
else:
record.name = ''

View File

@@ -66,11 +66,14 @@ class SfMaintenanceEquipmentOEE(models.Model):
if result['status'] == 1:
logs_list = result['data'][self.equipment_code]
logs_detail = ''
log_state = ''
for log in logs_list:
print('loooooooooooooooooooogs', log)
production_name = log['production_name'] if log['production_name'] else ' '
logs_detail += '<tr><td>' + log['time'] + '</td><td>' + log[
'state'] + '</td><td>' + production_name + '</td></tr>'
if log['state'] != log_state:
print('loooooooooooooooooooogs', log)
production_name = log['production_name'] if log['production_name'] else ' '
logs_detail += '<tr><td>' + log['time'] + '</td><td>' + log[
'state'] + '</td><td>' + production_name + '</td></tr>'
log_state = log['state']
# self.day_logs_detail = '<table><tr><th>时间</th><th>事件/状态</th><th>加工工单</th></tr>' + logs_detail + '</table>'
self.day_logs_detail = '''
<table border="1" style="border-collapse: collapse; width: 100%; text-align: center;">
@@ -119,10 +122,13 @@ class SfMaintenanceEquipmentOEE(models.Model):
if result['status'] == 1:
logs_list = result['data'][self.equipment_code]
logs_detail = ''
log_state = ''
for log in logs_list:
production_name = log['production_name'] if log['production_name'] else ' '
logs_detail += '<tr><td>' + log['time'] + '</td><td>' + log[
'state'] + '</td><td>' + production_name + '</td></tr>'
if log['state'] != log_state:
production_name = log['production_name'] if log['production_name'] else ' '
logs_detail += '<tr><td>' + log['time'] + '</td><td>' + log[
'state'] + '</td><td>' + production_name + '</td></tr>'
log_state = log['state']
# self.day_logs_detail = '<table><tr><th>时间</th><th>事件/状态</th><th>加工工单</th></tr>' + logs_detail + '</table>'
self.history_logs_detail = '''
<table border="1" style="border-collapse: collapse; width: 100%; text-align: center;">

View File

@@ -55,7 +55,31 @@
<group>
<button name="get_day_logs" type="object" string="查看24H日志" t-attf-style="white-space:nowrap;"/>
</group>
<field name="day_logs_detail"/>
<field name="day_logs_detail" readonly="1" widget="html"/>
<!-- <field name="page_num"/> -->
<!-- <group> -->
<!-- <group> -->
<!-- <button name="previous_day_logs" type="object" string="上一页" t-attf-style="white-space:nowrap;"/> -->
<!-- </group> -->
<!-- <group> -->
<!-- <button name="next_day_logs" type="object" string="下一页" t-attf-style="white-space:nowrap;"/> -->
<!-- </group> -->
<!-- </group> -->
<!-- <field name="day_logs_detail"/> -->
<!-- <field name="day_logs_detail" widget="html" nolabel="1"/> -->
<!-- <field name="day_logs_detail" widget="html" nolabel="1" class="oe_form_field oe_form_field_html"/> -->
<!-- <field name="day_logs_detail" widget="html" nolabel="1" class="oe_form_field oe_form_field_html" options='{"class": "o_form_readonly"}'/> -->
<!-- <field name="day_logs_detail" widget="html" nolabel="1" class="oe_form_field oe_form_field_html" options='{"class": "o_form_readonly", "readonly": true, "style": "width: 100%; height: 400px;"}'/> -->
<!-- <group> -->
<!-- <div class="oe_html_field"> -->
<!-- <div id="pagination_day_logs"> -->
<!-- <button id="prev_page_day_logs" disabled="true">Previous</button> -->
<!-- <button id="next_page_day_logs">Next</button> -->
<!-- </div> -->
<!-- </div> -->
<!-- </group> -->
</page>
<page string="历史日志详情">
<group>

View File

@@ -8,8 +8,8 @@
<field name="arch" type="xml">
<tree>
<field name="type" optional="hide"/>
<field name="brand"/>
<field name="maintenance_equipment_id"/>
<!-- <field name="brand"/> -->
<field name="name"/>
<field name="code_location" optional="hide"/>
<field name="fault_code" optional="hide"/>
<field name="alarm_time"/>
@@ -37,13 +37,14 @@
<sheet>
<div class="oe_title">
<h1>
<field name="code" readonly="1"/>
<field name="name" readonly="1"/>
</h1>
</div>
<group>
<group>
<!-- <field name="name"/> -->
<field name="code"/>
<!-- <field name="type" required="1" widget="radio" options="{'horizontal': true}"/> -->
<field name="maintenance_equipment_id"/>
<field name="brand"/>
@@ -57,7 +58,6 @@
</group>
<group>
<field name="operator"/>
<field name="fault_process"/>
<!-- <field name="alarm_way" required="1" widget="radio" options="{'horizontal': true}"/> -->
<field name="recovery_time"/>

View File

@@ -156,17 +156,26 @@ class AgvScheduling(models.Model):
if agv_site_state == '空闲':
# 查询终点接驳站为agv_site_id的AGV路线
task_routes = self.env['sf.agv.task.route'].sudo().search([('end_site_id', '=', agv_site_id)])
agv_scheduling = self.env['sf.agv.scheduling'].sudo().search(
agv_schedulings = self.env['sf.agv.scheduling'].sudo().search(
[('state', '=', '待下发'), ('agv_route_type', 'in', task_routes.mapped('route_type'))],
order='id asc',
limit=1
)
task_route = task_routes.filtered(
lambda r: r.start_site_id == agv_scheduling.start_site_id and r.start_site_id == agv_scheduling.start_site_id
)
if task_route:
# 下发AGV调度任务并修改接驳站状态为占用
agv_scheduling.dispatch_scheduling(task_route)
for agv_scheduling in agv_schedulings:
# 找到所有起点接驳站匹配的路线
start_matched_task_routes = task_routes.filtered(
lambda r: r.start_site_id == agv_scheduling.start_site_id
)
# 如果调度任务有终点接驳站,找到终点接驳站匹配的路线
if agv_scheduling.end_site_id:
matched_task_routes = start_matched_task_routes.filtered(
lambda r: r.end_site_id == agv_scheduling.end_site_id
)
else:
matched_task_routes = start_matched_task_routes
if matched_task_routes:
# 下发AGV调度任务并修改接驳站状态为占用
agv_scheduling.dispatch_scheduling(matched_task_routes[0])
break;
def _delivery_avg(self):
config = self.env['res.config.settings'].get_values()

View File

@@ -1082,6 +1082,12 @@ class ResMrpWorkOrder(models.Model):
# 重写工单开始按钮方法
def button_start(self):
if self.routing_type == 'CNC加工':
self.env['sf.production.plan'].sudo().search([('name', '=', self.production_id.name)]).write({
'state': 'processing',
'actual_start_time': datetime.now()
})
if self.routing_type == '装夹预调':
# 判断是否有坯料的序列号信息
boolean = False

View File

@@ -11,7 +11,7 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify','stock','sf_tool_management'],
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify','stock', 'sf_tool_management', 'sf_manufacturing', 'sf_hr'],
'data': [
'data/bussiness_node.xml',
],

View File

@@ -41,12 +41,20 @@ class SFMessageSale(models.Model):
# 继承并重写jikimo.message.dispatch的_get_message()
def _get_message(self, message_queue_ids):
res = super(SFMessageSale, self)._get_message(message_queue_ids)
if message_queue_ids.message_template_id.bussiness_node_id.name == '确认接单':
# sale_order = self.env['sale.order'].search([('id', '=', message_queue_ids.model.res_id)])
sale_order_line = self.env['sale.order.line'].search([('order_id', '=', int(message_queue_ids.res_id))])
if len(sale_order_line) == 1:
product = sale_order_line[0].product_id.name
elif len(sale_order_line) > 1:
product = '%s...' % sale_order_line[0].product_id.name
res[0] = res[0].replace('{{product_id}}', product)
new_res = []
processed_messages = set() # 用于跟踪已经处理过的消息
for item in message_queue_ids:
if item.message_template_id.bussiness_node_id.name == '确认接单':
sale_order_line = self.env['sale.order.line'].search([('order_id', '=', int(item.res_id))])
if len(sale_order_line) == 1:
product = sale_order_line[0].product_id.name
elif len(sale_order_line) > 1:
product = '%s...' % sale_order_line[0].product_id.name
for message in res:
message_text = message.replace('{{product_id}}', product)
if message_text not in processed_messages:
new_res.append(message_text)
processed_messages.add(message_text)
if new_res:
res = new_res
return res

View File

@@ -1,6 +1,10 @@
from odoo import models, fields, api, _
import logging, json
import requests
from odoo.addons.sf_base.commons.common import Common
from urllib.parse import urlencode
_logger = logging.getLogger(__name__)
class SFMessageWork(models.Model):
_name = 'mrp.workorder'
@@ -35,6 +39,28 @@ class SFMessageWork(models.Model):
contents.append(content)
return contents
@api.depends('cnc_ids.tool_state')
def _compute_tool_state(self):
# 将self的id与tool_state进行保存
tool_state_dict = {record.id: record.tool_state for record in self}
res = super(SFMessageWork, self)._compute_tool_state()
data = {'name': []}
for record in self:
if tool_state_dict[record.id] != '2' and record.tool_state == '2':
data['name'].append(record.production_id.programming_no)
if data['name']:
# 请求cloud接口发送微信消息推送
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/message/invalid_tool_state'
config_url = configsettings['sf_url'] + url
data['token'] = configsettings['token']
ret = requests.post(config_url, json=data, headers=config_header)
ret = ret.json()
_logger.info('无效用刀异常消息推送接口:%s' % ret)
return res
def request_url(self):
we_config_info = self.env['we.config'].sudo().search([], limit=1)
redirect_domain = self.env['we.app'].sudo().search([('id', '=', we_config_info.odoo_app_id.id)]).redirect_domain

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -278,6 +278,7 @@
sequence="150"
action="sf_production_plan_action"
groups="sf_base.group_plan_dispatch"
web_icon="sf_plan,static/description/计划.png"
/>
<!-- <record model="ir.ui.menu" id="mrp_custom_menu" inherit_id="mrp.menu_mrp_manufacturing"> -->

View File

@@ -74,6 +74,8 @@ class StockPicking(models.Model):
def send_to_bfm(self):
skip_backorder = self.env.context.get('skip_backorder')
cancel_backorder_ids = self.env.context.get('picking_ids_not_to_backorder')
# 下发发货到bfm
config = self.env['res.config.settings'].get_values()
move_ids, move_line_ids = self.deal_move_ids(self.move_ids, self.move_line_ids)
@@ -92,13 +94,13 @@ class StockPicking(models.Model):
'state': self.state,
'backorder_id': self.deal_send_backorder_id(self.backorder_id),
'backorder_ids': self.deal_send_backorder_id(self.backorder_ids),
'cancel_backorder_ids': skip_backorder,
'cancel_backorder_ids': True if skip_backorder and cancel_backorder_ids else False, # 没有欠单判断
'move_type': self.move_type,
},
}
url1 = config['bfm_url_new'] + '/api/stock/deliver_goods'
json_str = json.dumps(data)
print('json_str', json_str)
logging.info('json_str= %s', json_str)
r = requests.post(url1, json=data, data=None)
if r.status_code == 200:
result = json.loads(r.json()['result'])

View File

@@ -185,6 +185,7 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
_description = 'CAM工单程序用刀计划'
name = fields.Char('工单任务编号')
programming_no = fields.Char('编程单号')
cam_procedure_code = fields.Char('程序名')
filename = fields.Char('文件')
cam_cutter_spacing_code = fields.Char('刀号')
@@ -317,23 +318,33 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
"""
根据传入的工单信息查询是否有需要的功能刀具如果没有则生成CAM工单程序用刀计划
"""
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
'name': cnc_processing.workorder_id.production_id.name,
'cam_procedure_code': cnc_processing.program_name,
'filename': cnc_processing.cnc_id.name,
'functional_tool_name': cnc_processing.cutting_tool_name,
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
'process_type': cnc_processing.processing_type,
'margin_x_y': float(cnc_processing.margin_x_y),
'margin_z': float(cnc_processing.margin_z),
'finish_depth': float(cnc_processing.depth_of_processing_z),
'extension_length': float(cnc_processing.cutting_tool_extension_length),
'shank_model': cnc_processing.cutting_tool_handle_type,
'estimated_processing_time': cnc_processing.estimated_processing_time,
})
logging.info('CAM工单程序用刀计划创建成功')
# 创建装刀请求
knife_plan.apply_for_tooling()
# 获取编程单号
programming_no = cnc_processing.workorder_id.production_id.programming_no
logging.info(f'编程单号:{programming_no}')
cam_id = self.env['sf.cam.work.order.program.knife.plan'].sudo().search(
[('programming_no', '=', programming_no),
('functional_tool_name', '=', cnc_processing.cutting_tool_name)])
logging.info(f'CAM装刀计划{cam_id}')
if not cam_id:
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
'name': cnc_processing.workorder_id.production_id.name,
'programming_no': programming_no,
'cam_procedure_code': cnc_processing.program_name,
'filename': cnc_processing.cnc_id.name,
'functional_tool_name': cnc_processing.cutting_tool_name,
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
'process_type': cnc_processing.processing_type,
'margin_x_y': float(cnc_processing.margin_x_y),
'margin_z': float(cnc_processing.margin_z),
'finish_depth': float(cnc_processing.depth_of_processing_z),
'extension_length': float(cnc_processing.cutting_tool_extension_length),
'shank_model': cnc_processing.cutting_tool_handle_type,
'estimated_processing_time': cnc_processing.estimated_processing_time,
})
logging.info('CAM工单程序用刀计划创建成功')
# 创建装刀请求
knife_plan.apply_for_tooling()
def unlink_cam_plan(self, production):
for item in production:

View File

@@ -283,21 +283,22 @@
<field name="arch" type="xml">
<tree create="0">
<field name="name" string="工单编码"/>
<field name="cam_procedure_code"/>
<field name="filename"/>
<field name="programming_no"/>
<field name="cam_procedure_code" optional="hide"/>
<field name="filename" optional="hide"/>
<field name="functional_tool_name" string="刀具名称"/>
<field name="cam_cutter_spacing_code"/>
<field name="diameter" optional="hide"/>
<field name="tool_included_angle" optional="hide"/>
<field name="process_type"/>
<field name="process_type" optional="hide"/>
<field name="margin_x_y"/>
<field name="margin_z"/>
<field name="finish_depth"/>
<field name="extension_length" string="刀具伸出长度(mm)"/>
<field name="shank_model"/>
<field name="estimated_processing_time"/>
<field name="need_knife_time"/>
<field name="applicant_time"/>
<field name="need_knife_time" optional="hide"/>
<field name="applicant_time" optional="hide"/>
<field name="plan_execute_status"/>
<field name="production_line_id" invisible="1"/>
@@ -332,6 +333,7 @@
</div>
<group>
<group>
<field name="programming_no"/>
<field name="cam_procedure_code"/>
<field name="filename"/>
<field name="production_line_id"/>
@@ -388,6 +390,7 @@
<field name="model">sf.cam.work.order.program.knife.plan</field>
<field name="arch" type="xml">
<search>
<field name="programming_no"/>
<field name="name" string="工单编码"/>
<field name="cam_procedure_code"/>
<field name="filename"/>

View File

@@ -16,7 +16,7 @@
<record id="view_tool_inventory_inherit_search" model="ir.ui.view">
<field name="name">sf.tool.inventory.inherit.search</field>
<field name="model">sf.tool.inventory</field>
<field name="inherit_id" ref="sf_base.view_cutting_tool_material_search"/>
<field name="inherit_id" ref="sf_base.view_cutting_tool_inventory_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='extension']" position="after">
<searchpanel>

View File

@@ -1,25 +0,0 @@
# -*- 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

View File

@@ -1,70 +0,0 @@
# -*- 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,
}

View File

@@ -1,11 +0,0 @@
<?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>

View File

@@ -1,8 +0,0 @@
<?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>

View File

@@ -1,163 +0,0 @@
# -*- 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, {})

View File

@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
from . import ir_ui_view
from . import res_config

View File

@@ -1,18 +0,0 @@
# -*- 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)

View File

@@ -1,29 +0,0 @@
# -*- 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 "")

View File

@@ -1,2 +0,0 @@
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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_theme_data access.theme.data model_theme_data 1 1 1 1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 KiB

View File

@@ -1,444 +0,0 @@
<!-- 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
&amp; 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 &amp; 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 &amp; 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 -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 649 B

Some files were not shown because too many files have changed in this diff Show More