Add odex25_transactions

This commit is contained in:
expert 2024-06-24 14:26:02 +03:00
parent 3a3f834c54
commit 78a2e864a6
248 changed files with 23319 additions and 1 deletions

View File

@ -1,2 +1,2 @@
# odex25-standard-moduless
# odex25-standard-modules
This Repo contains general standard modules for all projects.

View File

@ -0,0 +1,29 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="Jinja2" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E265" />
<option value="E501" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N801" />
</list>
</option>
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/odex25_transactions.iml" filepath="$PROJECT_DIR$/.idea/odex25_transactions.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,4 @@
*pip install arabic-reshaper
*pip install python-bidi
pip install python-barcode

View File

@ -0,0 +1,4 @@
#-*- coding: utf-8 -*-
from . import models
from . import wizards
from . import controllers

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odex - Communications Management System.
# Copyright (C) 2020 Expert Co. Ltd. (<http://exp-sa.com>).
#
##############################################################################
{
'name' : 'C.M. Entity Sync',
'version' : '0.1',
'sequence' : 4,
'author' : 'Expert Co. Ltd.',
'category' : 'Odex25-Transactions/Odex25-Transactions',
'summary' : 'Correspondence Management, Entity Sync',
'description' : """
Odex - Correspondence Management, Entity Syncronization
========================================================
Intercompanies entity syncronization
""",
'website': 'http://www.exp-sa.com',
'depends': ['exp_transaction_documents'],
'data': [
'security/ir.model.access.csv',
# data
'data/data.xml',
# views
'views/entity_view.xml',
'views/settings_view.xml',
# wizards
'wizards/inter_entity_wizard.xml',
# actions amd menus
'views/actions_and_menus.xml',
],
'qweb' : [
],
'installable': True,
'auto_install': False,
'application': False,
}

View File

@ -0,0 +1,2 @@
#-*- coding: utf-8 -*-
from . import sync

View File

@ -0,0 +1,312 @@
#-*- coding: utf-8 -*-
import logging
import datetime
import base64
from odoo import http
from odoo.http import request
from odoo import models, api, fields, _,exceptions,SUPERUSER_ID
_logger = logging.getLogger(__name__)
class Sync(http.Controller):
@http.route('/cm/sync/broadcast', type='json', auth='public', methods=['POST'])
def add_new(self, **kw):
keys = [
'url',
'name',
'code',
'key',
'auth',
]
for k in keys:
if not kw.get(k, False):
return {
'error': 'Bad Request 1',
}
inter_entity = request.env['cm.inter_entity'].sudo()
if not inter_entity.authenticate(kw.get('auth', '')):
return {
'error': 'Bad Request 2',
'req': kw,
}
data = {
'url': kw.get('url', ''),
'name': kw.get('name', ''),
'code': kw.get('code', ''),
'uuid': kw.get('key', ''),
}
entities = inter_entity.add_new(data)
return {
'success': True,
'entities': entities,
}
@http.route('/cm/sync/update_from', type='json', auth='public', methods=['POST'])
def update_from(self, **kw):
keys = [
'key',
'auth',
]
for k in keys:
if not kw.get(k, False):
return {
'error': 'Bad Request 1',
}
inter_entity = request.env['cm.inter_entity'].sudo()
if not inter_entity.authenticate(kw.get('auth', '')):
return {
'error': 'Bad Request 2',
'req': kw,
}
return {
'success': True,
'entities': inter_entity.get_entities()
}
data = {
'url': kw.get('url', ''),
'name': kw.get('name', ''),
'code': kw.get('code', ''),
'uuid': kw.get('key', ''),
}
entities = inter_entity.add_new(data)
return {
'success': True,
'entities': entities,
}
@http.route('/cm/sync/new_key', type='json', auth='public', methods=['POST'])
def new_key(self, **kw):
keys = [
'old',
'key',
'auth',
]
for k in keys:
if not kw.get(k, False):
return {
'error': 'Bad Request 1',
}
inter_entity = request.env['cm.inter_entity'].sudo()
if not inter_entity.authenticate(kw.get('auth', '')):
return {
'error': 'Bad Request 2',
}
old = inter_entity.search([('uuid', '=', kw.get('old'))], limit=1)
if len(old):
data = {
'uuid': kw.get('key'),
'code': kw.get('key').split('-')[0],
}
old.write(data)
return {
'success': True,
}
@http.route('/cm/sync/update_entity', type='json', auth='public', methods=['POST'])
def update_entity(self, **kw):
keys = [
'data',
'auth',
]
for k in keys:
if not kw.get(k, False):
return {
'error': 'Bad Request 1',
}
inter_entity = request.env['cm.inter_entity'].sudo()
inter_entity_set = request.env['cm.inter_entity.sync'].sudo()
if not inter_entity.authenticate(kw.get('auth', '')):
return {
'error': 'Bad Request 2',
}
data = kw['data']
key = data['key']
entity = inter_entity.search([('uuid', '=', key)], limit=1)
ecode = data['details']['code']
is_new = data['details'].get('is_new', False)
if len(entity):
en = request.env['cm.entity'].sudo().search(
[('inter_entity_code', '=', ecode), ('inter_entity_id', '=', entity.id)])
if len(en):
d = data['details']['data']
d['broadcasted'] = True
en.write(d)
elif is_new:
d = data['details']['data']
dd = {
'name': d.get('name', ''),
# 'code': d.get('code', ''),
'type': 'external',
'is_inter_entity': True,
'inter_entity_code': d.get('inter_entity_code', ''),
'inter_entity_id': entity.id,
}
request.env['cm.entity'].sudo().create(dd)
else:
return {
'error': 'No Record'
}
return {
'success': True,
}
@http.route('/cm/sync/push_new', type='json', auth='public', methods=['POST'])
def push_new(self, **kw):
keys = [
'data',
'auth',
]
for k in keys:
if not kw.get(k, False):
return {
'error': 'Bad Request 1',
}
inter_entity = request.env['cm.inter_entity'].sudo()
if not inter_entity.authenticate(kw.get('auth', '')):
return {
'error': 'Bad Request 2',
}
data = kw['data']
key = data['key']
entity = inter_entity.search([('uuid', '=', key)], limit=1)
if len(entity):
d = data['data']
dd = {
'name': d.get('name', ''),
# 'code': d.get('code', ''),
'type': 'external',
'is_inter_entity': True,
'inter_entity_code': d.get('inter_entity_code', ''),
'inter_entity_id': entity.id,
}
try:
request.env['cm.entity'].sudo().with_context(
broacasted=True).create(dd)
return {
'success': True,
}
except Exception as e:
return {
'error': 'No Record'
}
else:
return {
'error': 'No Record'
}
return {
'success': True,
}
@http.route('/cm/sync/send_transaction', type='json', auth='public', methods=['POST'])
def send_transaction(self, **kw):
keys = [
'data',
'code',
'key',
'auth',
]
for k in keys:
if not kw.get(k, False):
return {
'error': 'Bad Request 1',
}
inter_entity = request.env['cm.inter_entity'].sudo()
Entity = request.env['cm.entity'].sudo()
if not inter_entity.authenticate(kw.get('auth', '')):
return {
'error': 'Bad Request 2',
}
data = kw['data']
code = kw['code']
key = kw['key']
auth = kw['auth']
from_id = data['from_id']
ie = inter_entity.search([('uuid', '=', auth)], limit=1)
extie = inter_entity.search([('uuid', '=', key)], limit=1)
setting = inter_entity.get_settings()
if not len(ie) or not len(extie):
return {
'error': 'No Record 1'
}
if not from_id:
from_id = Entity.search([('is_master_entity', '=', True),
('type', '=', 'external'), ('inter_entity_id', '=', extie.id)], limit=1)
if not len(from_id):
return {
'error': 'No Record 2'
}
else:
from_id = Entity.search([('inter_entity_code', '=', from_id), (
'type', '=', 'external'), ('inter_entity_id', '=', extie.id)], limit=1)
if not len(from_id):
return {
'error': 'No Record 3'
}
entity = Entity.search([('inter_entity_code', '=', code),('type', '=', 'unit')], limit=1)
if len(entity) > 0 and len(from_id) > 0:
Incoming = request.env['incoming.transaction'].sudo()
d = data
d['from_id'] = from_id.id
d['inter_entity_id'] = extie.id
d['important_id'] = setting.important_id.id
d['subject_type_id'] = setting.subject_type_id.id
if len(entity.secretary_id):
d['employee_id'] = entity.secretary_id.id
d['preparation_id'] = entity.id
else:
d['employee_id'] = setting.employee_id.id
d['preparation_id'] = setting.id
d['to_ids'] = [(4, entity.id), ]
# if entity.body:
# d['body'] = entity.body
d['due_date'] = fields.date.today()
attachment_rule_ids = d.get('attachment_rule_ids')
d.pop('attachment_rule_ids')
inc = Incoming.create(d)
for attachment in attachment_rule_ids:
attachement_rule = request.env['cm.attachment.rule'].sudo().create({
'employee_id': entity.secretary_id.id if len(entity.secretary_id) else setting.employee_id.id,
'entity_id': entity.id,
'file_save': attachment['file_save'],
'attachment_filename': attachment['attachment_filename'],
'incoming_transaction_id': inc.id if inc.id else False,
'date': attachment['date'],
'description': attachment['description'],
})
attachment_data = {
'name': attachment['attachment_filename'],
'datas_fname': attachment['attachment_filename'],
'datas': attachment['file_save'],
'res_model': 'cm.attachment.rule',
'res_id': attachement_rule.id,
}
request.env['ir.attachment'].sudo().create(attachment_data)
inc.preparation_id = inc.entity_id
# notification system and mailing
employee = inc.from_id
subj = _('Message Has been send !')
msg = _(u'{} &larr; {}.{}').format(employee and employee.name or '#',
u' / '.join([k.name for k in inc.to_ids]),
u'<a href="%s" >رابط المعاملة</a> ' % (inc.get_url()))
partner_ids = []
for partner in inc.to_ids:
if partner.type == 'unit':
partner_ids.append(partner.secretary_id.user_id.partner_id.id)
elif partner.type == 'employee':
partner_ids.append(partner.user_id.partner_id.id)
# thread_obj = request.env['mail.thread']
# thread_obj.message_post(type="notification", subject=subj, body=msg,
# partner_ids=partner_ids,
# subtype="mail.mt_comment")
# inc.send_message(template='exp_transaction_documents.incoming_notify_send_send_email')
else:
return {
'error': 'No Record 4'
}
return {
'success': True,
}

View File

@ -0,0 +1,18 @@
<openerp>
<data>
<record id="sync_settings" model="cm.inter_entity.sync"></record>
<!-- <record id="seq_type_cm_inter_entity" model="ir.sequence.type">-->
<!-- <field name="name">Entities</field>-->
<!-- <field name="code">cm.inter.entity</field>-->
<!-- </record>-->
<record id="seq_cm_inter_entity" model="ir.sequence">
<field name="name">Inter-Entities Seq</field>
<field name="code">cm.inter.entity</field>
<field name="prefix"></field>
<field name="suffix"></field>
<field name="padding">6</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,345 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * cm_entity_sync_odex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-16 03:24+0000\n"
"PO-Revision-Date: 2020-05-16 03:24+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,help:cm_entity_sync_odex.field_cm_entity_is_inter_entity
#: model:ir.model.fields,help:cm_entity_sync_odex.field_cm_entity_is_master_entity
msgid "\n"
" If checked, this entity will be syncronized by other entities in the group.\n"
" "
msgstr "\n"
" اذا تمّ اختياره، سيتم مشاركة المؤسسة مع بقية المؤسسات في المجموعة.\n"
" "
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/models/entity.py:278
#: code:addons/cm_entity_sync_odex/models/entity.py:280
#, python-format
msgid "Access Error"
msgstr "ليس لديك صلاحية !"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_activated
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_sync_config
msgid "Activate Syncronization"
msgstr "تفعيل نظام المزامنة"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_broadcast
msgid "Broadcast"
msgstr "مزامنة"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/wizards/wizards.py:34
#, python-format
msgid "Broadcast Error !Error While Broadcast new Server, please check server key and url."
msgstr "خطا في المزامنة ! خطا عند مزامنة مخدم الجديد , الرجاء التأكد من عنوان الويب او مفتاح المخدم."
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_inter_entity_update_wizard
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_inter_entity_wizard
msgid "Cancel"
msgstr "إلغاء"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_code
msgid "Code"
msgstr "رمز"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_create_uid
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_create_uid
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_create_uid
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_create_uid
msgid "Created by"
msgstr "أنشئ بواسطة"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_create_date
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_create_date
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_create_date
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_create_date
msgid "Created on"
msgstr "أنشئ في"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_server_id
msgid "Current Server"
msgstr "السيرفر (النظام) الحالي"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_employee_id
msgid "Default Created By"
msgstr "مدخل المعاملة الافتراضي"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_important_id
msgid "Default Important Degree"
msgstr "درجة الأهمية الافتراضية"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_subject_type_id
msgid "Default Transaction Type"
msgstr "نوع المعاملة الافتراضي"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_name
msgid "Description"
msgstr "الوصف"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_display_name
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_display_name
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_display_name
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_display_name
msgid "Display Name"
msgstr "الاسم المعروض"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_document_id
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_document_id
msgid "Document"
msgstr "مستند"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_entities
msgid "Entities"
msgstr "العناوين و الجهات"
#. module: cm_entity_sync_odex
#: model:ir.model,name:cm_entity_sync_odex.model_cm_inter_entity_sync
msgid "Entity Syncronization"
msgstr "الوحدات المتزامنه"
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_inter_entity_wizard
msgid "Generate"
msgstr "توليــد"
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_sync_config
msgid "Generate Key"
msgstr "توليد مفتاح مزامنة"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/models/settings.py:36
#, python-format
msgid "Generate new Key"
msgstr "توليد مفتاح مزامنة جديدة"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_id
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_id
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_id
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_id
msgid "ID"
msgstr "المعرف"
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_sync_config
msgid "Inter Entities"
msgstr "المؤسسات المتزامنة"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_incoming_transaction_inter_entity_id
msgid "Inter Entity"
msgstr "مؤسسة متزامنة"
#. module: cm_entity_sync_odex
#: model:ir.actions.act_window,name:cm_entity_sync_odex.cm_inter_entity_sync_action
#: model:ir.ui.menu,name:cm_entity_sync_odex.cm_inter_entity_sync_menu
msgid "Inter Entity Sync"
msgstr "إعدادات المؤسسات المتزامنة"
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.cm_inter_entity_tree
msgid "Inter-Entities"
msgstr "المؤسسات المتزامنة"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_entity_inter_entity_code
msgid "Inter-Entities Code"
msgstr "رمز التزامن"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_entity_inter_entity_id
msgid "Inter-Entities Ref"
msgstr "مرجع المؤسسة المتزامنة"
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.cm_inter_entity_form
msgid "Inter-Entity"
msgstr "مؤسسة متزامنة"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_entity_is_inter_entity
msgid "Is Inter-Entity ?"
msgstr "مؤسسة متزامنة ؟"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_entity_is_master_entity
msgid "Is Master-Entity ?"
msgstr "مؤسسة متزمنة رئيسية ؟"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity___last_update
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync___last_update
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard___last_update
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard___last_update
msgid "Last Modified on"
msgstr "آخر تعديل في"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_write_uid
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_write_uid
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_write_uid
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_write_uid
msgid "Last Updated by"
msgstr "آخر تحديث بواسطة"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_write_date
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_write_date
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_write_date
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_write_date
msgid "Last Updated on"
msgstr "آخر تحديث في"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_primary_id
msgid "Main Server"
msgstr "الخادم الرئيسي"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/controllers/sync.py:290
#, python-format
msgid "Message Has been send !"
msgstr "تم ارسال الرساله"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_uuid
msgid "Secret Key"
msgstr "الرمز السري"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_server_key
msgid "Server Key"
msgstr "مفتاح التزامن للسيرفر"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_url
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_server_url
msgid "Server URL"
msgstr "رابط السيرفر"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/models/entity.py:165
#: code:addons/cm_entity_sync_odex/models/entity.py:266
#: code:addons/cm_entity_sync_odex/models/entity.py:294
#, python-format
msgid "Sync Error Cannot Syncronize new key to Server {}, URL \"{}\""
msgstr "مشكلة في التزامن , غير قادر على مزامنة المفتاح الجديد Server {}, URL \"{}\""
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/models/entity.py:242
#, python-format
msgid "Sync Error Cannot send Transaction to server {}"
msgstr "مشكلة تزامن , لا يمكن ارسال المعاملة لمخدم {}"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/models/entity.py:150
#, python-format
msgid "Sync ErrorCannot Syncronize new key to Server {}, URL \"{}\""
msgstr "مشكلة في التزامن , غير قادر على مزامنة المفتاح الجديد Server {}, URL \"{}\""
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_sync_sync_key
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_wizard_key
msgid "Sync Key"
msgstr "مفتاح التزامن"
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_incoming_transaction_syncronized
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_outgoing_transaction_syncronized
msgid "Syncronized ?"
msgstr "متزامن ؟"
#. module: cm_entity_sync_odex
#: model:ir.model,name:cm_entity_sync_odex.model_cm_entity
msgid "Transactions Contacts"
msgstr "Transactions Contacts"
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_inter_entity_update_wizard
msgid "Update"
msgstr "تحديث الخوادم"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/models/settings.py:63
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_sync_config
#, python-format
msgid "Update From ..."
msgstr "تحديث من ..."
#. module: cm_entity_sync_odex
#: model:ir.model.fields,field_description:cm_entity_sync_odex.field_cm_inter_entity_update_wizard_server_id
msgid "Update Server"
msgstr "خادم التحديث"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/models/entity.py:280
#, python-format
msgid "You cannot change syncronized entity data !"
msgstr "لا يمكنك تعديل بيانات المؤسسات المتزامنة !"
#. module: cm_entity_sync_odex
#: model:ir.model,name:cm_entity_sync_odex.model_cm_inter_entity_update_wizard
msgid "cm.inter.entity.update.wizard"
msgstr "cm.inter.entity.update.wizard"
#. module: cm_entity_sync_odex
#: model:ir.model,name:cm_entity_sync_odex.model_cm_inter_entity_wizard
msgid "cm.inter.entity.wizard"
msgstr "cm.inter.entity.wizard"
#. module: cm_entity_sync_odex
#: model:ir.model,name:cm_entity_sync_odex.model_cm_inter_entity
msgid "cm.inter_entity"
msgstr "cm.inter_entity"
#. module: cm_entity_sync_odex
#: model:ir.model,name:cm_entity_sync_odex.model_incoming_transaction
msgid "incoming Transaction"
msgstr "معاملات الوارد الخارجي"
#. module: cm_entity_sync_odex
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_inter_entity_update_wizard
#: model:ir.ui.view,arch_db:cm_entity_sync_odex.view_cm_inter_entity_wizard
msgid "or"
msgstr "أو"
#. module: cm_entity_sync_odex
#: model:ir.model,name:cm_entity_sync_odex.model_outgoing_transaction
msgid "outgoing Transaction"
msgstr "معاملات الصادر الخارجي"
#. module: cm_entity_sync_odex
#: code:addons/cm_entity_sync_odex/controllers/sync.py:291
#, python-format
msgid "{} &larr; {}.{}"
msgstr "{} &larr; {}.{}"

View File

@ -0,0 +1,4 @@
#-*- coding: utf-8 -*-
from . import entity
from . import settings
from . import outgoing

View File

@ -0,0 +1,448 @@
#-*- coding: utf-8 -*-
import logging
import uuid
import requests
# from odoo import _, api, exceptions, fields, models
from odoo import models, api, fields,_
from odoo.exceptions import Warning,AccessError
_logger = logging.getLogger(__name__)
class EntityMatrix(models.Model):
_name = 'cm.inter_entity'
code = fields.Char(string='Code')
name = fields.Char(string='Description')
url = fields.Char(string='Server URL')
uuid = fields.Char(string='Secret Key')
@api.model
def generate_key(self):
key = uuid.uuid4()
return str(key)
@api.model
def broadcast(self, key=None, url=None, new_key=None):
if url.endswith('/'):
url = u'{}{}'.format(url[:-1], '/cm/sync/broadcast')
else:
url = u'{}{}'.format(url, '/cm/sync/broadcast')
code = new_key.split('-')[0]
company_name = self.env.user.company_id.name
new_url = self.env['ir.config_parameter'].sudo(
).get_param('web.base.url')
new_url = new_url.replace('http://', 'http://')
data = {
'code': code,
'name': company_name,
'url': new_url,
'key': new_key,
'auth': key,
}
try:
json = {
'jsonrpc': '2.0',
'method': 'call',
'id': None,
'params': data,
}
result = requests.post(url, json=json)
r = result.json()
if 'success' not in r.get('result', {}):
_logger.error(r)
return False
entities = r.get('result', {}).get('entities', [])
handle = []
cm_entity = self.env['cm.entity'].sudo()
for e in entities:
ie = self.sudo().create(e)
if len(e.get('entities', [])):
for ce in e.get('entities', []):
O = ce.copy()
O['inter_entity_id'] = ie.id
cm_entity.create(O)
T = {
'name': ie.name,
'inter_entity_code': e['uuid'].split('-')[0],
'type': 'external',
'is_inter_entity': True,
'is_master_entity': True,
'inter_entity_id': ie.id,
}
cm_entity.with_context(broadcasted=True).create(T)
handle.append((4, ie.id))
if handle:
sync = self.env['cm.inter_entity.sync'].sudo()
setting = sync.search([], limit=1)
setting.write({
'entities': handle,
})
except Exception as e:
_logger.error(e)
return False
return True
@api.model
def add_new(self, arch):
sync = self.env['cm.inter_entity.sync'].sudo()
setting = sync.search([], limit=1)
entities = self.sudo().get_entities()
entity = self.sudo().create(arch)
T = {
'name': arch['name'],
'inter_entity_code': arch['uuid'].split('-')[0],
'type': 'external',
'is_inter_entity': True,
'is_master_entity': True,
'inter_entity_id': entity.id,
}
self.env['cm.entity'].sudo().with_context(broadcasted=True).create(T)
setting.write({
'entities': [(4, entity.id)],
})
return entities
@api.model
def wrap_jsonrpc(self, data):
return {
'jsonrpc': '2.0',
'method': 'call',
'id': None,
'params': data,
}
@api.model
def post(self, url, data):
result = requests.post(url, json=data)
return result
@api.model
def is_success(self, result):
r = result.json() or {}
return 'success' in r.get('result', {})
@api.model
def get_settings(self):
sync = self.env['cm.inter_entity.sync'].sudo()
setting = sync.search([], limit=1)
return setting
@api.model
def sync_new_key(self, key):
sync = self.env['cm.inter_entity.sync'].sudo()
setting = sync.search([], limit=1)
SUBURL = u'/cm/sync/new_key'
for e in setting.entities.filtered(lambda k: k.uuid != setting.sync_key):
data = self.wrap_jsonrpc({
'key': key,
'old': setting.sync_key,
'auth': e.uuid,
})
url = u'{}{}'.format(e.url, SUBURL)
result = self.post(url, data)
if not self.is_success(result):
raise Warning(_('Sync Error'u'Cannot Syncronize new key to Server {}, URL "{}"').format(e.name, e.url))
return True
@api.model
def update_from(self, server):
setting = self.sudo().get_settings()
SUBURL = u'/cm/sync/update_from'
data = self.wrap_jsonrpc({
'auth': server.uuid,
'key': setting.sync_key,
})
url = u'{}{}'.format(server.url, SUBURL)
result = self.post(url, data)
if not self.is_success(result):
raise Warning(_('Sync Error Cannot Syncronize new key to Server {}, URL "{}"').format(server.name, server.url))
try:
r = result.json()
entities = r.get('result', {}).get('entities', [])
handle = []
cm_entity = self.env['cm.entity'].sudo()
for e in entities:
exists = setting.entities.filtered(lambda k: k.uuid == e['uuid'])
if len(exists):
exists.write(e)
continue
ie = self.sudo().create(e)
if len(e.get('entities', [])):
for ce in e.get('entities', []):
O = ce.copy()
O['inter_entity_id'] = ie.id
cm_entity.create(O)
try:
T = {
'name': ie.name,
'inter_entity_code': e['uuid'].split('-')[0],
'type': 'external',
'is_inter_entity': True,
'is_master_entity': True,
'inter_entity_id': ie.id,
}
cm_entity.with_context(broadcasted=True).create(T)
except Exception as e:
_logger.error(e)
handle.append((4, ie.id))
if handle:
sync = self.env['cm.inter_entity.sync'].sudo()
setting = sync.search([], limit=1)
setting.write({
'entities': handle,
})
except Exception as ee:
_logger.error(ee)
return False
return True
@api.model
def send_transaction(self, trasaction, instance, to_send):
setting = self.sudo().get_settings()
SUBURL = u'/cm/sync/send_transaction'
for e in to_send:
# trasaction['to_ids'] = e.inter_entity_code
from_id = False
K = instance.employee_id
K = K.parent_id
if K.is_inter_entity:
from_id = K
# while True:
# if not len(K.parent_id):
# break
# K = K.parent_id
# if K.is_inter_entity:
# _logger.warning(
# '----ccxxxxxxxxxxxxxxxxxxxxcccccccccccccccccccccccccccccccccccccccccccccccccccccc---------sendtransaction %s',
# K.is_inter_entity)
# from_id = K
# break
if from_id:
trasaction['from_id'] = from_id.inter_entity_code
else:
trasaction['from_id'] = False
data = self.wrap_jsonrpc({
'data': trasaction,
'key': setting.sync_key,
'code': e.inter_entity_code,
'auth': e.sudo().inter_entity_id.uuid,
})
url = u'{}{}'.format(e.sudo().inter_entity_id.url, SUBURL)
result = self.post(url, data)
# 'http://localhost:8069/cm/sync/send_transaction'
if not self.is_success(result):
_logger.error(result.json())
raise Warning(_('Sync Error Cannot send Transaction to server {}').format(e.name))
@api.model
def sync(self, entities):
raise NotImplementedError('Not NotImplemented !')
@api.model
def activate(self, arch):
raise NotImplementedError('Not NotImplemented !')
@api.model
def push_new(self, vals):
"""here we have bug"""
setting = self.sudo().get_settings()
SUBURL = u'/cm/sync/push_new'
for e in setting.entities.filtered(lambda k:
k.uuid != setting.sync_key):
data = self.wrap_jsonrpc({
'data': vals,
'auth': e.uuid,
})
url = u'{}{}'.format(e.url, SUBURL)
result = self.post(url, data)
if not self.is_success(result):
raise Warning(_('Sync Error Cannot Syncronize new key to Server {}, URL "{}"').format(e.name, e.url))
return True
@api.model
def clone(self, entity):
raise NotImplementedError('Not NotImplemented !')
@api.model
def update(self, vals):
setting = self.sudo().get_settings()
if vals['key'] != setting.sync_key:
if not vals.get('broadcasted', False) and not vals.get('dont_appear_in_send', False):
e = AccessError(_('Access Error'))
e.args = (
_('Access Error'), _('You cannot change syncronized entity data !'))
raise e
else:
return True
SUBURL = u'/cm/sync/update_entity'
for e in setting.entities.filtered(lambda k:
k.uuid != setting.sync_key):
data = self.wrap_jsonrpc({
'data': vals,
'auth': e.uuid,
})
url = u'{}{}'.format(e.url, SUBURL)
result = self.post(url, data)
if not self.is_success(result):
raise Warning(_('Sync Error Cannot Syncronize new key to Server {}, URL "{}"').format(e.name, e.url))
return True
@api.model
def remove(self, vals):
raise NotImplementedError('Not NotImplemented !')
@api.model
def get_cm_entities(self, e):
entity = self.env['cm.entity'].sudo()
entities = entity.search(
[('is_inter_entity', '=', True), ('inter_entity_id', '=', e.id)])
result = []
for ce in entities:
result.append({
'inter_entity_code': ce.inter_entity_code,
'name': ce.name,
'code': ce.code,
'type': 'external',
'is_inter_entity': True,
})
return result
@api.model
def get_entities(self):
sync = self.env['cm.inter_entity.sync'].sudo()
setting = sync.search([], limit=1)
data = []
for e in setting.entities:
if e.id == setting.server_id.id:
cm_entities = self.sudo().get_cm_entities(e)
data.append({
'name': e.name,
'code': e.code,
'uuid': e.uuid,
'url': e.url,
'entities': cm_entities,
})
return data
@api.model
def authenticate(self, key):
# add by Fatima 7/5/2020 for clean older code
"""to check key of entity before sync using in controller method receive key and return true if right key"""
sync = self.env['cm.inter_entity.sync'].sudo()
setting = sync.search([], limit=1)
try:
if setting.sync_key == key.strip():
return True
except Exception as e:
return False
return False
class Entity(models.Model):
_inherit = 'cm.entity'
inter_entity_id = fields.Many2one('cm.inter_entity', string='Inter-Entities Ref')
inter_entity_code = fields.Char(
string='Inter-Entities Code')
is_inter_entity = fields.Boolean(string='Is Inter-Entity ?', help='''
If checked, this entity will be syncronized by other entities in the group.
''')
is_master_entity = fields.Boolean(string='Is Master-Entity ?', help='''
If checked, this entity will be syncronized by other entities in the group.
''')
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
args = args or []
if not (name == '' and operator == 'ilike'):
search = self.env['cm.inter_entity'].sudo().search([('name', 'ilike', name)])
if len(search):
args += ['|', ('inter_entity_id', 'in', search.ids)]
return super(Entity, self).name_search(name=name, args=args, operator=operator, limit=limit)
def name_get(self):
result = []
for r in self:
if r.is_inter_entity:
if r.type == 'external':
result.append((r.id, u'{} \u21E6 {}'.format(
r.inter_entity_id.name or u'**', r.name)))
continue
result.append((r.id, r.name))
return result
@api.model
def create(self, vals):
setting = self.env['cm.inter_entity'].sudo().get_settings()
if vals.get('type', False) == 'unit' and vals.get('is_inter_entity', False):
if not vals.get('inter_entity_code', False):
vals['inter_entity_id'] = setting.server_id.id
vals['inter_entity_code'] = u'{}-{}'.format(setting.sync_key.split('-')[0] if setting.sync_key else False,
self.env['ir.sequence'].sudo().next_by_code('cm.inter.entity'))
obj = super(Entity, self).create(vals)
IE = self.env['cm.inter_entity'].sudo()
if vals.get('type', False) == 'unit' and vals.get('is_inter_entity', False):
if not self.env.context.get('broadcasted', False):
IE.push_new({
'data': vals,
'key': setting.sync_key,
})
return obj
# def unlink(self, cr, uid, ids, context=None):
# return super(Entity, self).unlink(cr, uid, ids, context=context)
def write(self, vals):
data = {}
values = vals.copy()
is_inter_entity = False
setting = self.env['cm.inter_entity'].sudo().get_settings()
if vals.get('is_inter_entity', False):
values['inter_entity_id'] = setting.server_id.id
values['inter_entity_code'] = u'{}-{}'.format(setting.sync_key.split(
'-')[0], self.env['ir.sequence'].sudo().next_by_code('cm.inter.entity'))
vals['inter_entity_id'] = values['inter_entity_id']
vals['inter_entity_code'] = values['inter_entity_code']
is_inter_entity = True
for r in self:
if r.is_inter_entity:
data = {
'details': {'code': r.inter_entity_code, 'data': vals},
'key': r.inter_entity_id.uuid,
'broadcasted': vals.get('broadcasted', False)
}
if not is_inter_entity:
data['details']['remove'] = True
elif is_inter_entity:
vals1 = r.read([])
vals1 = vals1[0]
lister = [
'name', 'inter_entity_code',
'is_inter_entity',
]
vals2, vals3 = {}, {}
for l in lister:
if l in vals1:
vals2[l] = vals1[l]
if l in vals:
vals3[l] = vals[l]
vals2.update(vals3)
if vals2:
code = vals2.get('inter_entity_code', r.inter_entity_code)
data = {
'details': {'code': code, 'data': vals2, 'is_new': True},
'key': setting.sync_key,
'broadcasted': vals.get('broadcasted', False)
}
u = super(Entity, self).write(values)
if data:
IE = self.env['cm.inter_entity'].sudo()
IE.update(data)
return u

View File

@ -0,0 +1,60 @@
#-*- coding: utf-8 -*-
import logging
import base64
from odoo import api, models, fields, _, exceptions
_logger = logging.getLogger(__name__)
class Transaction(models.Model):
_inherit = 'outgoing.transaction'
syncronized = fields.Boolean(string='Syncronized ?')
def action_draft(self):
super(Transaction, self).action_draft()
to_send = []
inter_entity = self.env['cm.inter_entity'].sudo()
for r in self:
r.write({
'syncronized': True,
})
for e in r.to_ids:
if e.is_inter_entity:
to_send.append(e)
attachment_rule_data = []
for attachment in r.attachment_rule_ids:
attachment_rule_data.append({
'id': attachment.id if attachment else '',
'file_save': attachment.file_save.decode('utf-8') if attachment.file_save else '',
'attachment_filename': u"".join(u'%s'%attachment.attachment_filename) if attachment.attachment_filename else '',
'outgoing_id': False,
'incoming_transaction_id': r.id if r else False,
'internal_id': False,
'date': attachment.date if attachment.date else '',
'description': attachment.description if attachment.description else '',
})
if len(to_send):
trasaction = {
# 'out_date': r.out_date,
'to_ids': None,
'type': 'new',
'subject': r.subject,
'incoming_number': r.name,
'incoming_date': r.transaction_date,
'syncronized': True,
'body': r.body,
'attachment_rule_ids': attachment_rule_data,
}
inter_entity.send_transaction(trasaction, r, to_send)
class Incoming(models.Model):
_inherit = 'incoming.transaction'
syncronized = fields.Boolean(string='Syncronized ?')
inter_entity_id = fields.Many2one('cm.inter_entity', string='Inter Entity')

View File

@ -0,0 +1,79 @@
#-*- coding: utf-8 -*-
'''Doc'''
import logging
from odoo import api, models, fields, _
_logger = logging.getLogger(__name__)
class Sync(models.Model):
'''Doc'''
_name = 'cm.inter_entity.sync'
_description = 'Entity Syncronization'
activated = fields.Boolean(string='Activate Syncronization')
sync_key = fields.Char(string='Sync Key', related='server_id.uuid')
server_id = fields.Many2one('cm.inter_entity', string='Current Server', store=True)
entities = fields.Many2many(
'cm.inter_entity', 'inter_entity_sync_rel', 'sync_id', 'inter_entity_id')
important_id = fields.Many2one(
'cm.transaction.important', string='Default Important Degree')
subject_type_id = fields.Many2one(
'cm.subject.type', string='Default Transaction Type')
employee_id = fields.Many2one(
'cm.entity', string='Default Created By')
def generate_key(self):
'''Doc'''
inter_entity = self.env['cm.inter_entity'].sudo()
for rec in self:
# if not len(rec.entities.filtered(lambda k: k.uuid != rec.sync_key)):
# first time
return {
'name': _('Generate new Key'),
'view_type': 'form',
'view_mode': 'form',
'res_id': False,
'res_model': 'cm.inter.entity.wizard',
'target': 'new',
'type': 'ir.actions.act_window',
'context': {
'default_document_id': rec.id,
'default_broadcast': True,
},
}
# k = inter_entity.generate_key()
# synced = inter_entity.sync_new_key(k)
# print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>synced",synced)
# if synced:
# rec.write({
# 'sync_key': k,
# })
def update_from(self):
'''Doc'''
inter_entity = self.env['cm.inter_entity'].sudo()
for rec in self:
if len(rec.entities.filtered(lambda k: k.uuid != rec.sync_key)):
# first time
return {
'name': _('Update From ...'),
'view_type': 'form',
'view_mode': 'form',
'res_id': False,
'res_model': 'cm.inter.entity.update.wizard',
'target': 'new',
'type': 'ir.actions.act_window',
'context': {
'default_document_id': rec.id,
'default_broadcast': True,
},
}
k = inter_entity.generate_key()
synced = inter_entity.sync_new_key(k)
if synced:
rec.write({
'sync_key': k,
})

View File

@ -0,0 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_inter_objects_manager,access_outgoing_inter_objects_manager,model_cm_inter_entity,exp_transaction_documents.group_cm_employee_group,1,1,1,1
access_inter_objects_reviewer,access_outgoing_inter_objects_reviewer_manager,model_cm_inter_entity,exp_transaction_documents.group_cm_user,1,1,1,1
access_inter_objects_unit_manager,access_outgoing_inter_objects_unit_manager,model_cm_inter_entity,exp_transaction_documents.group_cm_reviewer,1,1,1,1
access_inter_objects_dep,access_outgoing_inter_objects_dep,model_cm_inter_entity,exp_transaction_documents.group_cm_department_manager,1,1,1,1
access_outgoing_inter_objects_exe,access_outgoing_inter_objects_exe,model_cm_inter_entity,exp_transaction_documents.group_cm_executive_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_inter_objects_manager access_outgoing_inter_objects_manager model_cm_inter_entity exp_transaction_documents.group_cm_employee_group 1 1 1 1
3 access_inter_objects_reviewer access_outgoing_inter_objects_reviewer_manager model_cm_inter_entity exp_transaction_documents.group_cm_user 1 1 1 1
4 access_inter_objects_unit_manager access_outgoing_inter_objects_unit_manager model_cm_inter_entity exp_transaction_documents.group_cm_reviewer 1 1 1 1
5 access_inter_objects_dep access_outgoing_inter_objects_dep model_cm_inter_entity exp_transaction_documents.group_cm_department_manager 1 1 1 1
6 access_outgoing_inter_objects_exe access_outgoing_inter_objects_exe model_cm_inter_entity exp_transaction_documents.group_cm_executive_manager 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,15 @@
<odoo>
<data>
<record model="ir.actions.act_window" id="cm_inter_entity_sync_action">
<field name="name">Inter Entity Sync</field>
<field name="res_model">cm.inter_entity.sync</field>
<field name="view_mode">form</field>
<field name="res_id" ref="cm_entity_sync_odex.sync_settings"/>
</record>
<menuitem id="cm_inter_entity_sync_menu" name="Inter Entity Sync"
parent="exp_transaction_documents.cm_settings_menu" action="cm_inter_entity_sync_action" groups="base.group_no_one" />
</data>
</odoo>

View File

@ -0,0 +1,60 @@
<odoo>
<data>
<record id="cm_inter_entity_tree" model="ir.ui.view">
<field name="name">cm.inter_entity.tree</field>
<field name="model">cm.inter_entity</field>
<field name="arch" type="xml">
<tree string="Inter-Entities">
<field name="code"/>
<field name="name"/>
<field name="url"/>
<field name="uuid"/>
</tree>
</field>
</record>
<record id="cm_inter_entity_form" model="ir.ui.view">
<field name="name">cm.inter_entity.form</field>
<field name="model">cm.inter_entity</field>
<field name="arch" type="xml">
<form string="Inter-Entity">
<group>
<group>
<field name="code"/>
<field name="name"/>
</group>
<group>
<field name="url"/>
<field name="uuid"/>
</group>
</group>
</form>
</field>
</record>
<record id="cm_entity_form" model="ir.ui.view">
<field name="name">cm.entity.form</field>
<field name="model">cm.entity</field>
<field name="inherit_id" ref="exp_transaction_documents.cm_entity_form" />
<field name="arch" type="xml">
<field name="manager_id" position="after">
<field name="is_inter_entity" attrs="{'invisible':[('type','not in',['unit','external'])], 'readonly':[('type', '=','external')]}"/>
<field name="inter_entity_id" widget="selection" attrs="{'invisible':[('type','not in',['unit','external'])], 'readonly':True}"/>
<field name="inter_entity_code" attrs="{'invisible':[('type','not in',['unit','external'])], 'readonly':True}"/>
</field>
</field>
</record>
<!-- <record id="cm_entity_search" model="ir.ui.view">-->
<!-- <field name="name">cm.entity.filter.ext</field>-->
<!-- <field name="model">cm.entity</field>-->
<!-- <field name="inherit_id" ref="cm_odex.cm_entity_search" />-->
<!-- <field name="arch" type="xml">-->
<!-- <field name="name" position="attributes">-->
<!-- <attribute name="string">Entity</attribute>-->
<!-- <attribute name="filter_domain">['|','|','|','|','|','|','|',('name','ilike',self),('job_title_id.name', 'ilike', self),('parent_id.name', 'ilike', self),('manager_id.name', 'ilike', self),('partner_id.email', 'ilike', self),('partner_id.name', 'ilike', self),('code', 'ilike', self),('inter_entity_id.name', 'ilike', self)]</attribute>-->
<!-- </field>-->
<!-- </field>-->
<!-- </record>-->
</data>
</odoo>

View File

@ -0,0 +1,36 @@
<odoo>
<data>
<record id="view_cm_sync_config" model="ir.ui.view">
<field name="name">CM Settings Ext</field>
<field name="model">cm.inter_entity.sync</field>
<field name="arch" type="xml">
<form create="false" delete="false">
<group>
<group string="Activate Syncronization">
<field name="activated"/>
<field name="sync_key" attrs="{'readonly':True,'invisible':[('activated','=',False)]}"/>
<field name="server_id" attrs="{'readonly':True,'invisible':[('activated','=',False)]}"/>
<button string="Generate Key" type="object" name="generate_key" attrs="{'invisible':[('activated','=',False)]}" class="oe_highlight"/>
</group>
<group>
<field name="important_id" attrs="{'required':True}" widget="selection"/>
<field name="subject_type_id" attrs="{'required':True}" widget="selection"/>
<field name="employee_id" attrs="{'required':True}" domain="[('type','=','employee')]"/>
<button string="Update From ..." type="object" name="update_from" attrs="{'invisible':[('activated','=',False)]}"/>
</group>
</group>
<separator string="Inter Entities">
</separator>
<field name="entities" readonly="True">
<tree editable="bottom">
<field name="code"/>
<field name="name"/>
<field name="url"/>
</tree>
</field>
</form>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,2 @@
#-*- coding: utf-8 -*-
from . import wizards

View File

@ -0,0 +1,53 @@
<openerp>
<data>
<record id="view_cm_inter_entity_wizard" model="ir.ui.view">
<field name="name">CM - Inter entity Wizard</field>
<field name="model">cm.inter.entity.wizard</field>
<field name="arch" type="xml">
<form string="">
<group>
<group>
<field name="broadcast"/>
<field name="server_url" attrs="{'required': [('broadcast','=',True)],'invisible':[('broadcast','=',False)]}"/>
</group>
<group>
<field name="document_id" invisible="True" readonly="True"/>
<field name="key" invisible="True"/>
<field name="server_key" attrs="{'required': [('broadcast','=',True)],'invisible':[('broadcast','=',False)]}"/>
</group>
</group>
<footer>
<button string="Generate" type="object" name="generate" class="oe_highlight"/>
or
<button special="cancel" string="Cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
<record id="view_cm_inter_entity_update_wizard" model="ir.ui.view">
<field name="name">CM - Inter entity Update Wizard</field>
<field name="model">cm.inter.entity.update.wizard</field>
<field name="arch" type="xml">
<form string="">
<group>
<group>
<field name="server_id" domain="[('id','!=',primary_id)]" widget="selection" required="True"/>
</group>
<group>
<field name="primary_id" invisible="True" readonly="True"/>
<field name="document_id" invisible="True" readonly="True"/>
</group>
</group>
<footer>
<button string="Update" type="object" name="update" class="oe_highlight"/>
or
<button special="cancel" string="Cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
from odoo import api, models, fields, _, exceptions
import logging
_logger = logging.getLogger(__name__)
class AddNew(models.TransientModel):
_name = 'cm.inter.entity.wizard'
document_id = fields.Many2one('cm.inter_entity.sync', string='Document')
broadcast = fields.Boolean(string='Broadcast')
key = fields.Char(string='Sync Key')
server_url = fields.Char('Server URL')
server_key = fields.Char(string='Server Key')
def generate(self):
inter_entity = self.env['cm.inter_entity'].sudo()
for rec in self:
K = inter_entity.generate_key()
rec.key = K
company_code = K.split('-')[0]
company_name = self.env.user.company_id.name
company_url = self.env['ir.config_parameter'].sudo(
).get_param('web.base.url')
# company_url = company_url.replace('http://', 'https://')
synced = inter_entity.sync_new_key(K)
if synced:
rec.document_id.write({
'sync_key': K,
})
if rec.broadcast:
url = rec.server_url
key = rec.server_key
added = inter_entity.broadcast(url=url, key=key, new_key=K)
if not added:
raise exceptions.Warning(_('Broadcast Error !'
u'Error While Broadcast new Server, please check server key and url.'))
data = {
'name': company_name,
'code': company_code,
'url': company_url,
'uuid': K,
}
if len(rec.document_id.server_id):
rec.document_id.server_id.write({
'uuid': K,
'code': company_code,
'name': company_name,
'url': company_url,
})
else:
entity = inter_entity.create(data)
rec.document_id.write({
'server_id': entity.id,
'entities': [(4, entity.id)]
})
class Update(models.TransientModel):
_name = 'cm.inter.entity.update.wizard'
document_id = fields.Many2one('cm.inter_entity.sync', string='Document')
primary_id = fields.Many2one('cm.inter_entity', string='Main Server', related='document_id.server_id', store=True)
server_id = fields.Many2one('cm.inter_entity', string='Update Server')
def update(self):
for r in self:
setting = r.document_id
r.primary_id.sudo().update_from(r.server_id)

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import models

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odex - Communications Management System.
# Copyright (C) 2018 Expert Co. Ltd. (<http://exp-sa.com>).
#
##############################################################################
{
'name': 'Communications Management Barcodes Reports',
'version': '0.1',
'sequence': 4,
'author': 'Expert Co. Ltd.',
'category': 'Odex25-Transactions/Odex25-Transactions',
'summary': 'Correspondence Management System',
'description': """
Odex - Communications Management Reports
========================================
""",
'website': 'http://www.exp-sa.com',
'depends': ['exp_transaction_documents'],
'data': [
'reports/barcodes.xml',
'reports/extend_transaction_detail_report.xml',
'views/transactions_views.xml',
'views/assets.xml',
],
'qweb': [
],
'installable': True,
'auto_install': False,
'application': False,
}

View File

@ -0,0 +1,106 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * cm_odex_barcode
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-07-13 09:32+0000\n"
"PO-Revision-Date: 2020-07-13 09:32+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.extend_tran_header
#: model:ir.ui.view,arch_db:cm_odex_barcode.report_transaction_barcode
msgid "<span style=\"font-weight:bold\">Attachment:</span>"
msgstr "<span style=\"font-weight:bold\">المرفقات:</span>"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.report_transaction_barcode
msgid "<span style=\"font-weight:bold\">Date:</span>"
msgstr "<span style=\"font-weight:bold\">التاريخ:</span>"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.report_transaction_barcode
msgid "<span style=\"font-weight:bold\">Reference:</span>"
msgstr "<span style=\"font-weight:bold\">المرجع:</span>"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.extend_tran_header
msgid "<span style=\"font-weight:bold;\">Reference:</span>"
msgstr "<span style=\"font-weight:bold;\">المرجع:</span>"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.extend_tran_header
msgid "<span style=\"font-weight:bold;font-stretch: expanded;\">\n"
" Date:</span>"
msgstr "<span style=\"font-weight:bold;font-stretch: expanded;\">\n"
" التاريخ:</span>"
#. module: cm_odex_barcode
#: model:ir.model.fields,field_description:cm_odex_barcode.field_incoming_transaction_binary_barcode
#: model:ir.model.fields,field_description:cm_odex_barcode.field_internal_transaction_binary_barcode
#: model:ir.model.fields,field_description:cm_odex_barcode.field_outgoing_transaction_binary_barcode
#: model:ir.model.fields,field_description:cm_odex_barcode.field_transaction_transaction_binary_barcode
msgid "Barcode"
msgstr "باركود"
#. module: cm_odex_barcode
#. openerp-web
#: code:addons/cm_odex_barcode/static/src/js/cm_odex_barcode.js:47
#, python-format
msgid "Could not display the selected image."
msgstr "Could not display the selected image."
#. module: cm_odex_barcode
#. openerp-web
#: code:addons/cm_odex_barcode/static/src/js/cm_odex_barcode.js:47
#, python-format
msgid "Image"
msgstr "Image"
#. module: cm_odex_barcode
#: model:ir.actions.report,name:cm_odex_barcode.act_transaction_in_barcode
msgid "Incoming Transaction Barcode"
msgstr "باركود المعاملات الورادة"
#. module: cm_odex_barcode
#: model:ir.actions.report,name:cm_odex_barcode.act_transaction_internal_barcode
msgid "Internal Transaction Barcode"
msgstr "باركود المعاملات الداخلية"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.extend_tran_header
#: model:ir.ui.view,arch_db:cm_odex_barcode.report_transaction_barcode
msgid "Number:"
msgstr "رقم المعاملة:"
#. module: cm_odex_barcode
#: model:ir.actions.report,name:cm_odex_barcode.act_transaction_out_barcode
msgid "Outgoing Transaction Barcode"
msgstr "باركود المعاملات الصادره"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.form_incoming_barcode_print_button
#: model:ir.ui.view,arch_db:cm_odex_barcode.form_internal_barcode_print_button
#: model:ir.ui.view,arch_db:cm_odex_barcode.form_outgoing_barcode_print_button
msgid "Print Barcode"
msgstr "طباعة الباركود"
#. module: cm_odex_barcode
#: model:ir.ui.view,arch_db:cm_odex_barcode.form_incoming_barcode_print_button
#: model:ir.ui.view,arch_db:cm_odex_barcode.form_internal_barcode_print_button
#: model:ir.ui.view,arch_db:cm_odex_barcode.form_outgoing_barcode_print_button
msgid "Review Barcode"
msgstr "مراجعة الباركود"
#. module: cm_odex_barcode
#: model:ir.model,name:cm_odex_barcode.model_transaction_transaction
msgid "for common attribute between transaction"
msgstr "for common attribute between transaction"

View File

@ -0,0 +1,2 @@
from . import barcode
from . import arabic_reshaper

View File

@ -0,0 +1,359 @@
# -*- coding: utf-8 -*-
# This work is licensed under the GNU Public License (GPL).
# To view a copy of this license, visit http://www.gnu.org/copyleft/gpl.html
# Written by Abdullah Diab (mpcabd)
# Email: mpcabd@gmail.com
# Website: http://mpcabd.xyz
# Ported and tweaked from Java to Python, from Better Arabic Reshaper
# [https://github.com/agawish/Better-Arabic-Reshaper/]
# Usage:
# Install python-bidi [https://github.com/MeirKriheli/python-bidi], can be
# installed from pip `pip install python-bidi`.
# import arabic_reshaper
# from bidi.algorithm import get_display
# reshaped_text = arabic_reshaper.reshape(u'اللغة العربية رائعة')
# bidi_text = get_display(reshaped_text)
# Now you can pass `bidi_text` to any function that handles
# displaying/printing of the text, like writing it to PIL Image or passing
# it to a PDF generating method.
import re
DEFINED_CHARACTERS_ORGINAL_ALF_UPPER_MDD = u'\u0622'
DEFINED_CHARACTERS_ORGINAL_ALF_UPPER_HAMAZA = u'\u0623'
DEFINED_CHARACTERS_ORGINAL_ALF_LOWER_HAMAZA = u'\u0625'
DEFINED_CHARACTERS_ORGINAL_ALF = u'\u0627'
DEFINED_CHARACTERS_ORGINAL_LAM = u'\u0644'
LAM_ALEF_GLYPHS = [
[u'\u0622', u'\uFEF6', u'\uFEF5'],
[u'\u0623', u'\uFEF8', u'\uFEF7'],
[u'\u0627', u'\uFEFC', u'\uFEFB'],
[u'\u0625', u'\uFEFA', u'\uFEF9']
]
HARAKAT = [
u'\u0600', u'\u0601', u'\u0602', u'\u0603', u'\u0606', u'\u0607', u'\u0608', u'\u0609',
u'\u060A', u'\u060B', u'\u060D', u'\u060E', u'\u0610', u'\u0611', u'\u0612', u'\u0613',
u'\u0614', u'\u0615', u'\u0616', u'\u0617', u'\u0618', u'\u0619', u'\u061A', u'\u061B',
u'\u061E', u'\u061F', u'\u0621', u'\u063B', u'\u063C', u'\u063D', u'\u063E', u'\u063F',
u'\u0640', u'\u064B', u'\u064C', u'\u064D', u'\u064E', u'\u064F', u'\u0650', u'\u0651',
u'\u0652', u'\u0653', u'\u0654', u'\u0655', u'\u0656', u'\u0657', u'\u0658', u'\u0659',
u'\u065A', u'\u065B', u'\u065C', u'\u065D', u'\u065E', u'\u0660', u'\u066A', u'\u066B',
u'\u066C', u'\u066F', u'\u0670', u'\u0672', u'\u06D4', u'\u06D5', u'\u06D6', u'\u06D7',
u'\u06D8', u'\u06D9', u'\u06DA', u'\u06DB', u'\u06DC', u'\u06DF', u'\u06E0', u'\u06E1',
u'\u06E2', u'\u06E3', u'\u06E4', u'\u06E5', u'\u06E6', u'\u06E7', u'\u06E8', u'\u06E9',
u'\u06EA', u'\u06EB', u'\u06EC', u'\u06ED', u'\u06EE', u'\u06EF', u'\u06D6', u'\u06D7',
u'\u06D8', u'\u06D9', u'\u06DA', u'\u06DB', u'\u06DC', u'\u06DD', u'\u06DE', u'\u06DF',
u'\u06F0', u'\u06FD', u'\uFE70', u'\uFE71', u'\uFE72', u'\uFE73', u'\uFE74', u'\uFE75',
u'\uFE76', u'\uFE77', u'\uFE78', u'\uFE79', u'\uFE7A', u'\uFE7B', u'\uFE7C', u'\uFE7D',
u'\uFE7E', u'\uFE7F', u'\uFC5E', u'\uFC5F', u'\uFC60', u'\uFC61', u'\uFC62', u'\uFC63'
]
ARABIC_GLYPHS = {
u'\u0622': [u'\u0622', u'\uFE81', u'\uFE81', u'\uFE82', u'\uFE82', 2],
u'\u0623': [u'\u0623', u'\uFE83', u'\uFE83', u'\uFE84', u'\uFE84', 2],
u'\u0624': [u'\u0624', u'\uFE85', u'\uFE85', u'\uFE86', u'\uFE86', 2],
u'\u0625': [u'\u0625', u'\uFE87', u'\uFE87', u'\uFE88', u'\uFE88', 2],
u'\u0626': [u'\u0626', u'\uFE89', u'\uFE8B', u'\uFE8C', u'\uFE8A', 4],
u'\u0627': [u'\u0627', u'\u0627', u'\u0627', u'\uFE8E', u'\uFE8E', 2],
u'\u0628': [u'\u0628', u'\uFE8F', u'\uFE91', u'\uFE92', u'\uFE90', 4],
u'\u0629': [u'\u0629', u'\uFE93', u'\uFE93', u'\uFE94', u'\uFE94', 2],
u'\u062A': [u'\u062A', u'\uFE95', u'\uFE97', u'\uFE98', u'\uFE96', 4],
u'\u062B': [u'\u062B', u'\uFE99', u'\uFE9B', u'\uFE9C', u'\uFE9A', 4],
u'\u062C': [u'\u062C', u'\uFE9D', u'\uFE9F', u'\uFEA0', u'\uFE9E', 4],
u'\u062D': [u'\u062D', u'\uFEA1', u'\uFEA3', u'\uFEA4', u'\uFEA2', 4],
u'\u062E': [u'\u062E', u'\uFEA5', u'\uFEA7', u'\uFEA8', u'\uFEA6', 4],
u'\u062F': [u'\u062F', u'\uFEA9', u'\uFEA9', u'\uFEAA', u'\uFEAA', 2],
u'\u0630': [u'\u0630', u'\uFEAB', u'\uFEAB', u'\uFEAC', u'\uFEAC', 2],
u'\u0631': [u'\u0631', u'\uFEAD', u'\uFEAD', u'\uFEAE', u'\uFEAE', 2],
u'\u0632': [u'\u0632', u'\uFEAF', u'\uFEAF', u'\uFEB0', u'\uFEB0', 2],
u'\u0633': [u'\u0633', u'\uFEB1', u'\uFEB3', u'\uFEB4', u'\uFEB2', 4],
u'\u0634': [u'\u0634', u'\uFEB5', u'\uFEB7', u'\uFEB8', u'\uFEB6', 4],
u'\u0635': [u'\u0635', u'\uFEB9', u'\uFEBB', u'\uFEBC', u'\uFEBA', 4],
u'\u0636': [u'\u0636', u'\uFEBD', u'\uFEBF', u'\uFEC0', u'\uFEBE', 4],
u'\u0637': [u'\u0637', u'\uFEC1', u'\uFEC3', u'\uFEC4', u'\uFEC2', 4],
u'\u0638': [u'\u0638', u'\uFEC5', u'\uFEC7', u'\uFEC8', u'\uFEC6', 4],
u'\u0639': [u'\u0639', u'\uFEC9', u'\uFECB', u'\uFECC', u'\uFECA', 4],
u'\u063A': [u'\u063A', u'\uFECD', u'\uFECF', u'\uFED0', u'\uFECE', 4],
u'\u0641': [u'\u0641', u'\uFED1', u'\uFED3', u'\uFED4', u'\uFED2', 4],
u'\u0642': [u'\u0642', u'\uFED5', u'\uFED7', u'\uFED8', u'\uFED6', 4],
u'\u0643': [u'\u0643', u'\uFED9', u'\uFEDB', u'\uFEDC', u'\uFEDA', 4],
u'\u0644': [u'\u0644', u'\uFEDD', u'\uFEDF', u'\uFEE0', u'\uFEDE', 4],
u'\u0645': [u'\u0645', u'\uFEE1', u'\uFEE3', u'\uFEE4', u'\uFEE2', 4],
u'\u0646': [u'\u0646', u'\uFEE5', u'\uFEE7', u'\uFEE8', u'\uFEE6', 4],
u'\u0647': [u'\u0647', u'\uFEE9', u'\uFEEB', u'\uFEEC', u'\uFEEA', 4],
u'\u0648': [u'\u0648', u'\uFEED', u'\uFEED', u'\uFEEE', u'\uFEEE', 2],
u'\u0649': [u'\u0649', u'\uFEEF', u'\uFEEF', u'\uFEF0', u'\uFEF0', 2],
u'\u0671': [u'\u0671', u'\u0671', u'\u0671', u'\uFB51', u'\uFB51', 2],
u'\u064A': [u'\u064A', u'\uFEF1', u'\uFEF3', u'\uFEF4', u'\uFEF2', 4],
u'\u066E': [u'\u066E', u'\uFBE4', u'\uFBE8', u'\uFBE9', u'\uFBE5', 4],
u'\u06AA': [u'\u06AA', u'\uFB8E', u'\uFB90', u'\uFB91', u'\uFB8F', 4],
u'\u06C1': [u'\u06C1', u'\uFBA6', u'\uFBA8', u'\uFBA9', u'\uFBA7', 4],
u'\u06E4': [u'\u06E4', u'\u06E4', u'\u06E4', u'\u06E4', u'\uFEEE', 2],
u'\u067E': [u'\u067E', u'\uFB56', u'\uFB58', u'\uFB59', u'\uFB57', 4],
u'\u0698': [u'\u0698', u'\uFB8A', u'\uFB8A', u'\uFB8A', u'\uFB8B', 2],
u'\u06AF': [u'\u06AF', u'\uFB92', u'\uFB94', u'\uFB95', u'\uFB93', 4],
u'\u0686': [u'\u0686', u'\uFB7A', u'\uFB7C', u'\uFB7D', u'\uFB7B', 4],
u'\u06A9': [u'\u06A9', u'\uFB8E', u'\uFB90', u'\uFB91', u'\uFB8F', 4],
u'\u06CC': [u'\u06CC', u'\uFEEF', u'\uFEF3', u'\uFEF4', u'\uFEF0', 4]
}
ARABIC_GLYPHS_LIST = [
[u'\u0622', u'\uFE81', u'\uFE81', u'\uFE82', u'\uFE82', 2],
[u'\u0623', u'\uFE83', u'\uFE83', u'\uFE84', u'\uFE84', 2],
[u'\u0624', u'\uFE85', u'\uFE85', u'\uFE86', u'\uFE86', 2],
[u'\u0625', u'\uFE87', u'\uFE87', u'\uFE88', u'\uFE88', 2],
[u'\u0626', u'\uFE89', u'\uFE8B', u'\uFE8C', u'\uFE8A', 4],
[u'\u0627', u'\u0627', u'\u0627', u'\uFE8E', u'\uFE8E', 2],
[u'\u0628', u'\uFE8F', u'\uFE91', u'\uFE92', u'\uFE90', 4],
[u'\u0629', u'\uFE93', u'\uFE93', u'\uFE94', u'\uFE94', 2],
[u'\u062A', u'\uFE95', u'\uFE97', u'\uFE98', u'\uFE96', 4],
[u'\u062B', u'\uFE99', u'\uFE9B', u'\uFE9C', u'\uFE9A', 4],
[u'\u062C', u'\uFE9D', u'\uFE9F', u'\uFEA0', u'\uFE9E', 4],
[u'\u062D', u'\uFEA1', u'\uFEA3', u'\uFEA4', u'\uFEA2', 4],
[u'\u062E', u'\uFEA5', u'\uFEA7', u'\uFEA8', u'\uFEA6', 4],
[u'\u062F', u'\uFEA9', u'\uFEA9', u'\uFEAA', u'\uFEAA', 2],
[u'\u0630', u'\uFEAB', u'\uFEAB', u'\uFEAC', u'\uFEAC', 2],
[u'\u0631', u'\uFEAD', u'\uFEAD', u'\uFEAE', u'\uFEAE', 2],
[u'\u0632', u'\uFEAF', u'\uFEAF', u'\uFEB0', u'\uFEB0', 2],
[u'\u0633', u'\uFEB1', u'\uFEB3', u'\uFEB4', u'\uFEB2', 4],
[u'\u0634', u'\uFEB5', u'\uFEB7', u'\uFEB8', u'\uFEB6', 4],
[u'\u0635', u'\uFEB9', u'\uFEBB', u'\uFEBC', u'\uFEBA', 4],
[u'\u0636', u'\uFEBD', u'\uFEBF', u'\uFEC0', u'\uFEBE', 4],
[u'\u0637', u'\uFEC1', u'\uFEC3', u'\uFEC4', u'\uFEC2', 4],
[u'\u0638', u'\uFEC5', u'\uFEC7', u'\uFEC8', u'\uFEC6', 4],
[u'\u0639', u'\uFEC9', u'\uFECB', u'\uFECC', u'\uFECA', 4],
[u'\u063A', u'\uFECD', u'\uFECF', u'\uFED0', u'\uFECE', 4],
[u'\u0641', u'\uFED1', u'\uFED3', u'\uFED4', u'\uFED2', 4],
[u'\u0642', u'\uFED5', u'\uFED7', u'\uFED8', u'\uFED6', 4],
[u'\u0643', u'\uFED9', u'\uFEDB', u'\uFEDC', u'\uFEDA', 4],
[u'\u0644', u'\uFEDD', u'\uFEDF', u'\uFEE0', u'\uFEDE', 4],
[u'\u0645', u'\uFEE1', u'\uFEE3', u'\uFEE4', u'\uFEE2', 4],
[u'\u0646', u'\uFEE5', u'\uFEE7', u'\uFEE8', u'\uFEE6', 4],
[u'\u0647', u'\uFEE9', u'\uFEEB', u'\uFEEC', u'\uFEEA', 4],
[u'\u0648', u'\uFEED', u'\uFEED', u'\uFEEE', u'\uFEEE', 2],
[u'\u0649', u'\uFEEF', u'\uFEEF', u'\uFEF0', u'\uFEF0', 2],
[u'\u0671', u'\u0671', u'\u0671', u'\uFB51', u'\uFB51', 2],
[u'\u064A', u'\uFEF1', u'\uFEF3', u'\uFEF4', u'\uFEF2', 4],
[u'\u066E', u'\uFBE4', u'\uFBE8', u'\uFBE9', u'\uFBE5', 4],
[u'\u06AA', u'\uFB8E', u'\uFB90', u'\uFB91', u'\uFB8F', 4],
[u'\u06C1', u'\uFBA6', u'\uFBA8', u'\uFBA9', u'\uFBA7', 4],
[u'\u067E', u'\uFB56', u'\uFB58', u'\uFB59', u'\uFB57', 4],
[u'\u0698', u'\uFB8A', u'\uFB8A', u'\uFB8A', u'\uFB8B', 2],
[u'\u06AF', u'\uFB92', u'\uFB94', u'\uFB95', u'\uFB93', 4],
[u'\u0686', u'\uFB7A', u'\uFB7C', u'\uFB7D', u'\uFB7B', 4],
[u'\u06A9', u'\uFB8E', u'\uFB90', u'\uFB91', u'\uFB8F', 4],
[u'\u06CC', u'\uFEEF', u'\uFEF3', u'\uFEF4', u'\uFEF0', 4]
]
def get_reshaped_glyph(target, location):
if target in ARABIC_GLYPHS:
return ARABIC_GLYPHS[target][location]
else:
return target
def get_glyph_type(target):
if target in ARABIC_GLYPHS:
return ARABIC_GLYPHS[target][5]
else:
return 2
def is_haraka(target):
return target in HARAKAT
def replace_jalalah(unshaped_word):
return re.sub(u'^\u0627\u0644\u0644\u0647$', u'\uFDF2', unshaped_word)
def replace_lam_alef(unshaped_word):
list_word = list(unshaped_word)
letter_before = u''
for i in range(len(unshaped_word)):
if not is_haraka(unshaped_word[i]) and unshaped_word[i] != DEFINED_CHARACTERS_ORGINAL_LAM:
letter_before = unshaped_word[i]
if unshaped_word[i] == DEFINED_CHARACTERS_ORGINAL_LAM:
candidate_lam = unshaped_word[i]
lam_position = i
haraka_position = i + 1
while haraka_position < len(unshaped_word) and is_haraka(unshaped_word[haraka_position]):
haraka_position += 1
if haraka_position < len(unshaped_word):
if lam_position > 0 and get_glyph_type(letter_before) > 2:
lam_alef = get_lam_alef(
list_word[haraka_position], candidate_lam, False)
else:
lam_alef = get_lam_alef(
list_word[haraka_position], candidate_lam, True)
if lam_alef != '':
list_word[lam_position] = lam_alef
list_word[haraka_position] = u' '
return u''.join(list_word).replace(u' ', u'')
def get_lam_alef(candidate_alef, candidate_lam, is_end_of_word):
shift_rate = 1
reshaped_lam_alef = u''
if is_end_of_word:
shift_rate += 1
if DEFINED_CHARACTERS_ORGINAL_LAM == candidate_lam:
if DEFINED_CHARACTERS_ORGINAL_ALF_UPPER_MDD == candidate_alef:
reshaped_lam_alef = LAM_ALEF_GLYPHS[0][shift_rate]
if DEFINED_CHARACTERS_ORGINAL_ALF_UPPER_HAMAZA == candidate_alef:
reshaped_lam_alef = LAM_ALEF_GLYPHS[1][shift_rate]
if DEFINED_CHARACTERS_ORGINAL_ALF == candidate_alef:
reshaped_lam_alef = LAM_ALEF_GLYPHS[2][shift_rate]
if DEFINED_CHARACTERS_ORGINAL_ALF_LOWER_HAMAZA == candidate_alef:
reshaped_lam_alef = LAM_ALEF_GLYPHS[3][shift_rate]
return reshaped_lam_alef
class DecomposedWord(object):
def __init__(self, word):
self.stripped_harakat = []
self.harakat_positions = []
self.stripped_regular_letters = []
self.letters_position = []
for i in range(len(word)):
c = word[i]
if is_haraka(c):
self.harakat_positions.append(i)
self.stripped_harakat.append(c)
else:
self.letters_position.append(i)
self.stripped_regular_letters.append(c)
def reconstruct_word(self, reshaped_word):
l = list(u'\0' * (len(self.stripped_harakat) + len(reshaped_word)))
for i in range(len(self.letters_position)):
l[self.letters_position[i]] = reshaped_word[i]
for i in range(len(self.harakat_positions)):
l[self.harakat_positions[i]] = self.stripped_harakat[i]
return u''.join(l)
def get_reshaped_word(unshaped_word):
unshaped_word = replace_jalalah(unshaped_word)
unshaped_word = replace_lam_alef(unshaped_word)
decomposed_word = DecomposedWord(unshaped_word)
result = u''
if decomposed_word.stripped_regular_letters:
result = reshape_it(u''.join(decomposed_word.stripped_regular_letters))
return decomposed_word.reconstruct_word(result)
def reshape_it(unshaped_word):
if not unshaped_word:
return u''
if len(unshaped_word) == 1:
return get_reshaped_glyph(unshaped_word[0], 1)
reshaped_word = []
for i in range(len(unshaped_word)):
before = False
after = False
if i == 0:
after = get_glyph_type(unshaped_word[i]) == 4
elif i == len(unshaped_word) - 1:
before = get_glyph_type(unshaped_word[i - 1]) == 4
else:
after = get_glyph_type(unshaped_word[i]) == 4
before = get_glyph_type(unshaped_word[i - 1]) == 4
if after and before:
reshaped_word.append(get_reshaped_glyph(unshaped_word[i], 3))
elif after and not before:
reshaped_word.append(get_reshaped_glyph(unshaped_word[i], 2))
elif not after and before:
reshaped_word.append(get_reshaped_glyph(unshaped_word[i], 4))
elif not after and not before:
reshaped_word.append(get_reshaped_glyph(unshaped_word[i], 1))
return u''.join(reshaped_word)
def is_arabic_character(target):
return target in ARABIC_GLYPHS or target in HARAKAT
def get_words(sentence):
if sentence:
return re.split('\\s', sentence)
return []
def has_arabic_letters(word):
for c in word:
if is_arabic_character(c):
return True
return False
def is_arabic_word(word):
for c in word:
if not is_arabic_character(c):
return False
return True
def get_words_from_mixed_word(word):
temp_word = u''
words = []
for c in word:
if is_arabic_character(c):
if temp_word and not is_arabic_word(temp_word):
words.append(temp_word)
temp_word = c
else:
temp_word += c
else:
if temp_word and is_arabic_word(temp_word):
words.append(temp_word)
temp_word = c
else:
temp_word += c
if temp_word:
words.append(temp_word)
return words
def reshape(text):
if text:
lines = re.split('\\r?\\n', text)
for i in range(len(lines)):
lines[i] = reshape_sentence(lines[i])
return u'\n'.join(lines)
return u''
def reshape_sentence(sentence):
words = get_words(sentence)
for i in range(len(words)):
word = words[i]
if has_arabic_letters(word):
if is_arabic_word(word):
words[i] = get_reshaped_word(word)
else:
mixed_words = get_words_from_mixed_word(word)
for j in range(len(mixed_words)):
mixed_words[j] = get_reshaped_word(mixed_words[j])
words[i] = u''.join(mixed_words)
return u' '.join(words)

View File

@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
# import sys
#
# # reload(sys)
# # sys.setdefaultencoding("utf-8")
import base64
import os
from odoo.exceptions import ValidationError
# import barcode as barcode
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from io import BytesIO
from pathlib import Path
from odoo.modules.module import get_module_resource
from lxml import etree
import arabic_reshaper
from bidi.algorithm import get_display
from odoo import models, api, fields
from odoo.tools.translate import _
# from odoo.osv.orm import setup_modifiers
class Transaction(models.Model):
_inherit = 'transaction.transaction'
binary_barcode = fields.Binary(string='Barcode', attachment=True)
@api.constrains('ean13', 'name', 'transaction_date', 'type')
def binary_compute_constraint(self):
fonts = [os.path.dirname(__file__) + '/img/KacstOffice.ttf',
os.path.dirname(__file__) + '/img/amiri-regular.ttf']
img = Image.new("RGBA", (500, 420), "white")
draw = ImageDraw.Draw(img)
number_word = "الرقم : "
number_word_reshaped = arabic_reshaper.reshape(
u'' + number_word)
number_word_artext = get_display(number_word_reshaped)
draw.text((220, 20),
number_word_artext, "black",
font=ImageFont.truetype(fonts[1], 18))
number_value = self.name
number_value_reshaped = arabic_reshaper.reshape(
u'' + number_value if number_value else '')
number_value_artext = get_display(number_value_reshaped)
draw.text((80, 20),
number_value_artext, "black",
font=ImageFont.truetype(fonts[1], 18))
#
date_hijri = "التاريخ : "
date_hijri_reshaped = arabic_reshaper.reshape(
u'' + date_hijri)
date_hijri_artext = get_display(date_hijri_reshaped)
draw.text((211, 40),
date_hijri_artext, "black",
font=ImageFont.truetype(fonts[1], 18))
date_hijri_value = self.transaction_date_hijri
date_hijri_value_reshaped = arabic_reshaper.reshape(
u'' + date_hijri_value if date_hijri_value else '')
date_hijri_artext = get_display(date_hijri_value_reshaped)
draw.text((120, 40),
date_hijri_artext.replace('-', '/'), "black",
font=ImageFont.truetype(fonts[1], 18))
date_m = "الموافق : "
date_m_reshaped = arabic_reshaper.reshape(
u'' + date_m)
date_m_artext = get_display(date_m_reshaped)
draw.text((210, 65),
date_m_artext, "black",
font=ImageFont.truetype(fonts[1], 18))
date_m_value = self.transaction_date
date_m_value_reshaped = arabic_reshaper.reshape(
u'' + str(date_m_value) if date_m_value else '')
date_m_value_artext = get_display(date_m_value_reshaped)
draw.text((120, 65),
date_m_value_artext.replace('-', '/'), "black",
font=ImageFont.truetype(fonts[1], 18))
attach_m = "المرفقات : "
attach_m_reshaped = arabic_reshaper.reshape(
u'' + attach_m)
date_m_artext = get_display(attach_m_reshaped)
draw.text((200, 85),
date_m_artext, "black",
font=ImageFont.truetype(fonts[1], 18))
attach_m_value = str(self.attachment_num) if self.attachment_num else '0'
attach_m_value_reshaped = arabic_reshaper.reshape(
u'' + attach_m_value)
attach_mvalue_artext = get_display(attach_m_value_reshaped)
draw.text((180, 85),
attach_mvalue_artext, "black",
font=ImageFont.truetype(fonts[1], 18))
# barcode_symbology = options.get('symbology', 'Code128')
barcode = self.env['ir.actions.report'].barcode('Code11', self.name, width=250, height=100,
humanreadable=0)
barcode_buffer = BytesIO(barcode)
barcode_image_file = Image.open(barcode_buffer)
ImageDraw.Draw(img)
buffered = BytesIO()
img.paste(barcode_image_file, (20, 110))
img.save(buffered, format="png")
img_str = base64.b64encode(buffered.getvalue())
self.binary_barcode = img_str
class AttachmentInherit(models.Model):
_inherit = 'ir.attachment'
# @api.constrains('vals_list')
# def create(self, vals_list):
# print("***********")
# res = super(AttachmentInherit, self).create(vals_list)
# print(res.mimetype)
# if res.mimetype == 'text/html':
# raise ValidationError(_('You cannot inset a html File'))
#
# return res

View File

@ -0,0 +1,86 @@
#!/usr/bin/env python
# This file is part of python-bidi
#
# python-bidi is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Copyright (C) 2008-2010 Yaacov Zamir <kzamir_a_walla.co.il>,
# Copyright (C) 2010-2015 Meir kriheli <mkriheli@gmail.com>.
"""
Implementation of Unicode Bidirectional Algorithm
http://www.unicode.org/unicode/reports/tr9/
"""
VERSION = '0.4.0'
def main():
"""Will be used to create the console script"""
import optparse
import sys
import codecs
import locale
import six
from .algorithm import get_display
parser = optparse.OptionParser()
parser.add_option('-e', '--encoding',
dest='encoding',
default='utf-8',
type='string',
help='Text encoding (default: utf-8)')
parser.add_option('-u', '--upper-is-rtl',
dest='upper_is_rtl',
default=False,
action='store_true',
help="Treat upper case chars as strong 'R' "
'for debugging (default: False).')
parser.add_option('-d', '--debug',
dest='debug',
default=False,
action='store_true',
help="Output to stderr steps taken with the algorithm")
parser.add_option('-b', '--base-dir',
dest='base_dir',
default=None,
type='string',
help="Override base direction [L|R]")
options, rest = parser.parse_args()
if options.base_dir and options.base_dir not in 'LR':
parser.error('option -b can be L or R')
# allow unicode in sys.stdout.write
if six.PY2:
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
if rest:
lines = rest
else:
lines = sys.stdin
for line in lines:
display = get_display(line, options.encoding, options.upper_is_rtl,
options.base_dir, options.debug)
# adjust the encoding as unicode, to match the output encoding
if not isinstance(display, six.text_type):
display = display.decode(options.encoding)
six.print_(display, end='')

View File

@ -0,0 +1,658 @@
# This file is part of python-bidi
#
# python-bidi is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Copyright (C) 2008-2010 Yaacov Zamir <kzamir_a_walla.co.il>,
# Copyright (C) 2010-2015 Meir kriheli <mkriheli@gmail.com>.
"bidirectional alogrithm implementation"
import sys
import inspect
from collections import deque
from unicodedata import bidirectional, mirrored
import six
from .mirror import MIRRORED
# Some definitions
PARAGRAPH_LEVELS = {'L': 0, 'AL': 1, 'R': 1}
EXPLICIT_LEVEL_LIMIT = 62
def _LEAST_GREATER_ODD(x):
return (x + 1) | 1
def _LEAST_GREATER_EVEN(x):
return (x + 2) & ~1
X2_X5_MAPPINGS = {
'RLE': (_LEAST_GREATER_ODD, 'N'),
'LRE': (_LEAST_GREATER_EVEN, 'N'),
'RLO': (_LEAST_GREATER_ODD, 'R'),
'LRO': (_LEAST_GREATER_EVEN, 'L'),
}
# Added 'B' so X6 won't execute in that case and X8 will run it's course
X6_IGNORED = list(X2_X5_MAPPINGS.keys()) + ['BN', 'PDF', 'B']
X9_REMOVED = list(X2_X5_MAPPINGS.keys()) + ['BN', 'PDF']
def _embedding_direction(x):
return ('L', 'R')[x % 2]
_IS_UCS2 = sys.maxunicode == 65535
_SURROGATE_MIN, _SURROGATE_MAX = 55296, 56319 # D800, DBFF
def debug_storage(storage, base_info=False, chars=True, runs=False):
"Display debug information for the storage"
import codecs
import locale
import sys
if six.PY2:
stderr = codecs.getwriter(locale.getpreferredencoding())(sys.stderr)
else:
stderr = sys.stderr
caller = inspect.stack()[1][3]
stderr.write('in %s\n' % caller)
if base_info:
stderr.write(u' base level : %d\n' % storage['base_level'])
stderr.write(u' base dir : %s\n' % storage['base_dir'])
if runs:
stderr.write(u' runs : %s\n' % list(storage['runs']))
if chars:
output = u' Chars : '
for _ch in storage['chars']:
if _ch != '\n':
output += _ch['ch']
else:
output += 'C'
stderr.write(output + u'\n')
output = u' Res. levels : %s\n' % u''.join(
[six.text_type(_ch['level']) for _ch in storage['chars']])
stderr.write(output)
_types = [_ch['type'].ljust(3) for _ch in storage['chars']]
for i in range(3):
if i:
output = u' %s\n'
else:
output = u' Res. types : %s\n'
stderr.write(output % u''.join([_t[i] for _t in _types]))
def get_base_level(text, upper_is_rtl=False):
"""Get the paragraph base embedding level. Returns 0 for LTR,
1 for RTL.
`text` a unicode object.
Set `upper_is_rtl` to True to treat upper case chars as strong 'R'
for debugging (default: False).
"""
base_level = None
prev_surrogate = False
# P2
for _ch in text:
# surrogate in case of ucs2
if _IS_UCS2 and (_SURROGATE_MIN <= ord(_ch) <= _SURROGATE_MAX):
prev_surrogate = _ch
continue
elif prev_surrogate:
_ch = prev_surrogate + _ch
prev_surrogate = False
# treat upper as RTL ?
if upper_is_rtl and _ch.isupper():
base_level = 1
break
bidi_type = bidirectional(_ch)
if bidi_type in ('AL', 'R'):
base_level = 1
break
elif bidi_type == 'L':
base_level = 0
break
# P3
if base_level is None:
base_level = 0
return base_level
def get_embedding_levels(text, storage, upper_is_rtl=False, debug=False):
"""Get the paragraph base embedding level and direction,
set the storage to the array of chars"""
prev_surrogate = False
base_level = storage['base_level']
# preset the storage's chars
for _ch in text:
if _IS_UCS2 and (_SURROGATE_MIN <= ord(_ch) <= _SURROGATE_MAX):
prev_surrogate = _ch
continue
elif prev_surrogate:
_ch = prev_surrogate + _ch
prev_surrogate = False
if upper_is_rtl and _ch.isupper():
bidi_type = 'R'
else:
bidi_type = bidirectional(_ch)
storage['chars'].append({
'ch': _ch,
'level': base_level,
'type': bidi_type,
'orig': bidi_type
})
if debug:
debug_storage(storage, base_info=True)
def explicit_embed_and_overrides(storage, debug=False):
"""Apply X1 to X9 rules of the unicode algorithm.
See http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions
"""
overflow_counter = almost_overflow_counter = 0
directional_override = 'N'
levels = deque()
# X1
embedding_level = storage['base_level']
for _ch in storage['chars']:
bidi_type = _ch['type']
level_func, override = X2_X5_MAPPINGS.get(bidi_type, (None, None))
if level_func:
# So this is X2 to X5
# if we've past EXPLICIT_LEVEL_LIMIT, note it and do nothing
if overflow_counter != 0:
overflow_counter += 1
continue
new_level = level_func(embedding_level)
if new_level < EXPLICIT_LEVEL_LIMIT:
levels.append((embedding_level, directional_override))
embedding_level, directional_override = new_level, override
elif embedding_level == EXPLICIT_LEVEL_LIMIT - 2:
# The new level is invalid, but a valid level can still be
# achieved if this level is 60 and we encounter an RLE or
# RLO further on. So record that we 'almost' overflowed.
almost_overflow_counter += 1
else:
overflow_counter += 1
else:
# X6
if bidi_type not in X6_IGNORED:
_ch['level'] = embedding_level
if directional_override != 'N':
_ch['type'] = directional_override
# X7
elif bidi_type == 'PDF':
if overflow_counter:
overflow_counter -= 1
elif almost_overflow_counter and \
embedding_level != EXPLICIT_LEVEL_LIMIT - 1:
almost_overflow_counter -= 1
elif levels:
embedding_level, directional_override = levels.pop()
# X8
elif bidi_type == 'B':
levels.clear()
overflow_counter = almost_overflow_counter = 0
embedding_level = _ch['level'] = storage['base_level']
directional_override = 'N'
# Removes the explicit embeds and overrides of types
# RLE, LRE, RLO, LRO, PDF, and BN. Adjusts extended chars
# next and prev as well
# Applies X9. See http://unicode.org/reports/tr9/#X9
storage['chars'] = [_ch for _ch in storage['chars']
if _ch['type'] not in X9_REMOVED]
calc_level_runs(storage)
if debug:
debug_storage(storage, runs=True)
def calc_level_runs(storage):
"""Split the storage to run of char types at the same level.
Applies X10. See http://unicode.org/reports/tr9/#X10
"""
# run level depends on the higher of the two levels on either side of
# the boundary If the higher level is odd, the type is R; otherwise,
# it is L
storage['runs'].clear()
chars = storage['chars']
# empty string ?
if not chars:
return
def calc_level_run(b_l, b_r):
return ['L', 'R'][max(b_l, b_r) % 2]
first_char = chars[0]
sor = calc_level_run(storage['base_level'], first_char['level'])
eor = None
run_start = run_length = 0
prev_level, prev_type = first_char['level'], first_char['type']
for _ch in chars:
curr_level, curr_type = _ch['level'], _ch['type']
if curr_level == prev_level:
run_length += 1
else:
eor = calc_level_run(prev_level, curr_level)
storage['runs'].append({'sor': sor, 'eor': eor, 'start': run_start,
'type': prev_type, 'length': run_length})
sor = eor
run_start += run_length
run_length = 1
prev_level, prev_type = curr_level, curr_type
# for the last char/runlevel
eor = calc_level_run(curr_level, storage['base_level'])
storage['runs'].append({'sor': sor, 'eor': eor, 'start': run_start,
'type': curr_type, 'length': run_length})
def resolve_weak_types(storage, debug=False):
"""Reslove weak type rules W1 - W3.
See: http://unicode.org/reports/tr9/#Resolving_Weak_Types
"""
for run in storage['runs']:
prev_strong = prev_type = run['sor']
start, length = run['start'], run['length']
chars = storage['chars'][start:start + length]
for _ch in chars:
# W1. Examine each nonspacing mark (NSM) in the level run, and
# change the type of the NSM to the type of the previous character.
# If the NSM is at the start of the level run, it will get the type
# of sor.
bidi_type = _ch['type']
if bidi_type == 'NSM':
_ch['type'] = bidi_type = prev_type
# W2. Search backward from each instance of a European number until
# the first strong type (R, L, AL, or sor) is found. If an AL is
# found, change the type of the European number to Arabic number.
if bidi_type == 'EN' and prev_strong == 'AL':
_ch['type'] = 'AN'
# update prev_strong if needed
if bidi_type in ('R', 'L', 'AL'):
prev_strong = bidi_type
prev_type = _ch['type']
# W3. Change all ALs to R
for _ch in chars:
if _ch['type'] == 'AL':
_ch['type'] = 'R'
# W4. A single European separator between two European numbers changes
# to a European number. A single common separator between two numbers of
# the same type changes to that type.
for idx in range(1, len(chars) - 1):
bidi_type = chars[idx]['type']
prev_type = chars[idx - 1]['type']
next_type = chars[idx + 1]['type']
if bidi_type == 'ES' and (prev_type == next_type == 'EN'):
chars[idx]['type'] = 'EN'
if bidi_type == 'CS' and prev_type == next_type and \
prev_type in ('AN', 'EN'):
chars[idx]['type'] = prev_type
# W5. A sequence of European terminators adjacent to European numbers
# changes to all European numbers.
for idx in range(len(chars)):
if chars[idx]['type'] == 'EN':
for et_idx in range(idx - 1, -1, -1):
if chars[et_idx]['type'] == 'ET':
chars[et_idx]['type'] = 'EN'
else:
break
for et_idx in range(idx + 1, len(chars)):
if chars[et_idx]['type'] == 'ET':
chars[et_idx]['type'] = 'EN'
else:
break
# W6. Otherwise, separators and terminators change to Other Neutral.
for _ch in chars:
if _ch['type'] in ('ET', 'ES', 'CS'):
_ch['type'] = 'ON'
# W7. Search backward from each instance of a European number until the
# first strong type (R, L, or sor) is found. If an L is found, then
# change the type of the European number to L.
prev_strong = run['sor']
for _ch in chars:
if _ch['type'] == 'EN' and prev_strong == 'L':
_ch['type'] = 'L'
if _ch['type'] in ('L', 'R'):
prev_strong = _ch['type']
if debug:
debug_storage(storage, runs=True)
def resolve_neutral_types(storage, debug):
"""Resolving neutral types. Implements N1 and N2
See: http://unicode.org/reports/tr9/#Resolving_Neutral_Types
"""
for run in storage['runs']:
start, length = run['start'], run['length']
# use sor and eor
chars = [{'type': run['sor']}] + storage['chars'][start:start + length] + \
[{'type': run['eor']}]
total_chars = len(chars)
seq_start = None
for idx in range(total_chars):
_ch = chars[idx]
if _ch['type'] in ('B', 'S', 'WS', 'ON'):
# N1. A sequence of neutrals takes the direction of the
# surrounding strong text if the text on both sides has the same
# direction. European and Arabic numbers act as if they were R
# in terms of their influence on neutrals. Start-of-level-run
# (sor) and end-of-level-run (eor) are used at level run
# boundaries.
if seq_start is None:
seq_start = idx
prev_bidi_type = chars[idx - 1]['type']
else:
if seq_start is not None:
next_bidi_type = chars[idx]['type']
if prev_bidi_type in ('AN', 'EN'):
prev_bidi_type = 'R'
if next_bidi_type in ('AN', 'EN'):
next_bidi_type = 'R'
for seq_idx in range(seq_start, idx):
if prev_bidi_type == next_bidi_type:
chars[seq_idx]['type'] = prev_bidi_type
else:
# N2. Any remaining neutrals take the embedding
# direction. The embedding direction for the given
# neutral character is derived from its embedding
# level: L if the character is set to an even level,
# and R if the level is odd.
chars[seq_idx]['type'] = \
_embedding_direction(chars[seq_idx]['level'])
seq_start = None
if debug:
debug_storage(storage)
def resolve_implicit_levels(storage, debug):
"""Resolving implicit levels (I1, I2)
See: http://unicode.org/reports/tr9/#Resolving_Implicit_Levels
"""
for run in storage['runs']:
start, length = run['start'], run['length']
chars = storage['chars'][start:start + length]
for _ch in chars:
# only those types are allowed at this stage
assert _ch['type'] in ('L', 'R', 'EN', 'AN'), \
'%s not allowed here' % _ch['type']
if _embedding_direction(_ch['level']) == 'L':
# I1. For all characters with an even (left-to-right) embedding
# direction, those of type R go up one level and those of type
# AN or EN go up two levels.
if _ch['type'] == 'R':
_ch['level'] += 1
elif _ch['type'] != 'L':
_ch['level'] += 2
else:
# I2. For all characters with an odd (right-to-left) embedding
# direction, those of type L, EN or AN go up one level.
if _ch['type'] != 'R':
_ch['level'] += 1
if debug:
debug_storage(storage, runs=True)
def reverse_contiguous_sequence(chars, line_start, line_end, highest_level,
lowest_odd_level):
"""L2. From the highest level found in the text to the lowest odd
level on each line, including intermediate levels not actually
present in the text, reverse any contiguous sequence of characters
that are at that level or higher.
"""
for level in range(highest_level, lowest_odd_level - 1, -1):
_start = _end = None
for run_idx in range(line_start, line_end + 1):
run_ch = chars[run_idx]
if run_ch['level'] >= level:
if _start is None:
_start = _end = run_idx
else:
_end = run_idx
else:
if _end:
chars[_start:+_end + 1] = \
reversed(chars[_start:+_end + 1])
_start = _end = None
# anything remaining ?
if _start is not None:
chars[_start:+_end + 1] = \
reversed(chars[_start:+_end + 1])
def reorder_resolved_levels(storage, debug):
"""L1 and L2 rules"""
# Applies L1.
should_reset = True
chars = storage['chars']
for _ch in chars[::-1]:
# L1. On each line, reset the embedding level of the following
# characters to the paragraph embedding level:
if _ch['orig'] in ('B', 'S'):
# 1. Segment separators,
# 2. Paragraph separators,
_ch['level'] = storage['base_level']
should_reset = True
elif should_reset and _ch['orig'] in ('BN', 'WS'):
# 3. Any sequence of whitespace characters preceding a segment
# separator or paragraph separator
# 4. Any sequence of white space characters at the end of the
# line.
_ch['level'] = storage['base_level']
else:
should_reset = False
max_len = len(chars)
# L2 should be per line
# Calculates highest level and loweset odd level on the fly.
line_start = line_end = 0
highest_level = 0
lowest_odd_level = EXPLICIT_LEVEL_LIMIT
for idx in range(max_len):
_ch = chars[idx]
# calc the levels
char_level = _ch['level']
if char_level > highest_level:
highest_level = char_level
if char_level % 2 and char_level < lowest_odd_level:
lowest_odd_level = char_level
if _ch['orig'] == 'B' or idx == max_len - 1:
line_end = idx
# omit line breaks
if _ch['orig'] == 'B':
line_end -= 1
reverse_contiguous_sequence(chars, line_start, line_end,
highest_level, lowest_odd_level)
# reset for next line run
line_start = idx + 1
highest_level = 0
lowest_odd_level = EXPLICIT_LEVEL_LIMIT
if debug:
debug_storage(storage)
def apply_mirroring(storage, debug):
"""Applies L4: mirroring
See: http://unicode.org/reports/tr9/#L4
"""
# L4. A character is depicted by a mirrored glyph if and only if (a) the
# resolved directionality of that character is R, and (b) the
# Bidi_Mirrored property value of that character is true.
for _ch in storage['chars']:
unichar = _ch['ch']
if mirrored(unichar) and \
_embedding_direction(_ch['level']) == 'R':
_ch['ch'] = MIRRORED.get(unichar, unichar)
if debug:
debug_storage(storage)
def get_empty_storage():
"""Return an empty storage skeleton, usable for testing"""
return {
'base_level': None,
'base_dir': None,
'chars': [],
'runs': deque(),
}
def get_display(unicode_or_str, encoding='utf-8', upper_is_rtl=False,
base_dir=None, debug=False):
"""Accepts unicode or string. In case it's a string, `encoding`
is needed as it works on unicode ones (default:"utf-8").
Set `upper_is_rtl` to True to treat upper case chars as strong 'R'
for debugging (default: False).
Set `base_dir` to 'L' or 'R' to override the calculated base_level.
Set `debug` to True to display (using sys.stderr) the steps taken with the
algorithm.
Returns the display layout, either as unicode or `encoding` encoded
string.
"""
storage = get_empty_storage()
# utf-8 ? we need unicode
if isinstance(unicode_or_str, six.text_type):
text = unicode_or_str
decoded = False
else:
text = unicode_or_str.decode(encoding)
decoded = True
if base_dir is None:
base_level = get_base_level(text, upper_is_rtl)
else:
base_level = PARAGRAPH_LEVELS[base_dir]
storage['base_level'] = base_level
storage['base_dir'] = ('L', 'R')[base_level]
get_embedding_levels(text, storage, upper_is_rtl, debug)
explicit_embed_and_overrides(storage, debug)
resolve_weak_types(storage, debug)
resolve_neutral_types(storage, debug)
resolve_implicit_levels(storage, debug)
reorder_resolved_levels(storage, debug)
apply_mirroring(storage, debug)
chars = storage['chars']
display = u''.join([_ch['ch'] for _ch in chars])
if decoded:
return display.encode(encoding)
else:
return display

View File

@ -0,0 +1,388 @@
# This file is part of python-bidi
#
# python-bidi is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Copyright (C) 2008-2010 Yaacov Zamir <kzamir_a_walla.co.il>,
# Copyright (C) 2010-2015 Meir kriheli <mkriheli@gmail.com>.
"""Mirrored chars"""
# Can't seem to get this data from python's unicode data, so this is imported
# from http://www.unicode.org/Public/UNIDATA/BidiMirroring.txt
MIRRORED = {
u'\u0028': u'\u0029', # LEFT PARENTHESIS
u'\u0029': u'\u0028', # RIGHT PARENTHESIS
u'\u003C': u'\u003E', # LESS-THAN SIGN
u'\u003E': u'\u003C', # GREATER-THAN SIGN
u'\u005B': u'\u005D', # LEFT SQUARE BRACKET
u'\u005D': u'\u005B', # RIGHT SQUARE BRACKET
u'\u007B': u'\u007D', # LEFT CURLY BRACKET
u'\u007D': u'\u007B', # RIGHT CURLY BRACKET
u'\u00AB': u'\u00BB', # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
u'\u00BB': u'\u00AB', # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
u'\u0F3A': u'\u0F3B', # TIBETAN MARK GUG RTAGS GYON
u'\u0F3B': u'\u0F3A', # TIBETAN MARK GUG RTAGS GYAS
u'\u0F3C': u'\u0F3D', # TIBETAN MARK ANG KHANG GYON
u'\u0F3D': u'\u0F3C', # TIBETAN MARK ANG KHANG GYAS
u'\u169B': u'\u169C', # OGHAM FEATHER MARK
u'\u169C': u'\u169B', # OGHAM REVERSED FEATHER MARK
u'\u2039': u'\u203A', # SINGLE LEFT-POINTING ANGLE QUOTATION MARK
u'\u203A': u'\u2039', # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
u'\u2045': u'\u2046', # LEFT SQUARE BRACKET WITH QUILL
u'\u2046': u'\u2045', # RIGHT SQUARE BRACKET WITH QUILL
u'\u207D': u'\u207E', # SUPERSCRIPT LEFT PARENTHESIS
u'\u207E': u'\u207D', # SUPERSCRIPT RIGHT PARENTHESIS
u'\u208D': u'\u208E', # SUBSCRIPT LEFT PARENTHESIS
u'\u208E': u'\u208D', # SUBSCRIPT RIGHT PARENTHESIS
u'\u2208': u'\u220B', # ELEMENT OF
u'\u2209': u'\u220C', # NOT AN ELEMENT OF
u'\u220A': u'\u220D', # SMALL ELEMENT OF
u'\u220B': u'\u2208', # CONTAINS AS MEMBER
u'\u220C': u'\u2209', # DOES NOT CONTAIN AS MEMBER
u'\u220D': u'\u220A', # SMALL CONTAINS AS MEMBER
u'\u2215': u'\u29F5', # DIVISION SLASH
u'\u223C': u'\u223D', # TILDE OPERATOR
u'\u223D': u'\u223C', # REVERSED TILDE
u'\u2243': u'\u22CD', # ASYMPTOTICALLY EQUAL TO
u'\u2252': u'\u2253', # APPROXIMATELY EQUAL TO OR THE IMAGE OF
u'\u2253': u'\u2252', # IMAGE OF OR APPROXIMATELY EQUAL TO
u'\u2254': u'\u2255', # COLON EQUALS
u'\u2255': u'\u2254', # EQUALS COLON
u'\u2264': u'\u2265', # LESS-THAN OR EQUAL TO
u'\u2265': u'\u2264', # GREATER-THAN OR EQUAL TO
u'\u2266': u'\u2267', # LESS-THAN OVER EQUAL TO
u'\u2267': u'\u2266', # GREATER-THAN OVER EQUAL TO
u'\u2268': u'\u2269', # [BEST FIT] LESS-THAN BUT NOT EQUAL TO
u'\u2269': u'\u2268', # [BEST FIT] GREATER-THAN BUT NOT EQUAL TO
u'\u226A': u'\u226B', # MUCH LESS-THAN
u'\u226B': u'\u226A', # MUCH GREATER-THAN
u'\u226E': u'\u226F', # [BEST FIT] NOT LESS-THAN
u'\u226F': u'\u226E', # [BEST FIT] NOT GREATER-THAN
u'\u2270': u'\u2271', # [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO
u'\u2271': u'\u2270', # [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO
u'\u2272': u'\u2273', # [BEST FIT] LESS-THAN OR EQUIVALENT TO
u'\u2273': u'\u2272', # [BEST FIT] GREATER-THAN OR EQUIVALENT TO
u'\u2274': u'\u2275', # [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO
u'\u2275': u'\u2274', # [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO
u'\u2276': u'\u2277', # LESS-THAN OR GREATER-THAN
u'\u2277': u'\u2276', # GREATER-THAN OR LESS-THAN
u'\u2278': u'\u2279', # [BEST FIT] NEITHER LESS-THAN NOR GREATER-THAN
u'\u2279': u'\u2278', # [BEST FIT] NEITHER GREATER-THAN NOR LESS-THAN
u'\u227A': u'\u227B', # PRECEDES
u'\u227B': u'\u227A', # SUCCEEDS
u'\u227C': u'\u227D', # PRECEDES OR EQUAL TO
u'\u227D': u'\u227C', # SUCCEEDS OR EQUAL TO
u'\u227E': u'\u227F', # [BEST FIT] PRECEDES OR EQUIVALENT TO
u'\u227F': u'\u227E', # [BEST FIT] SUCCEEDS OR EQUIVALENT TO
u'\u2280': u'\u2281', # [BEST FIT] DOES NOT PRECEDE
u'\u2281': u'\u2280', # [BEST FIT] DOES NOT SUCCEED
u'\u2282': u'\u2283', # SUBSET OF
u'\u2283': u'\u2282', # SUPERSET OF
u'\u2284': u'\u2285', # [BEST FIT] NOT A SUBSET OF
u'\u2285': u'\u2284', # [BEST FIT] NOT A SUPERSET OF
u'\u2286': u'\u2287', # SUBSET OF OR EQUAL TO
u'\u2287': u'\u2286', # SUPERSET OF OR EQUAL TO
u'\u2288': u'\u2289', # [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO
u'\u2289': u'\u2288', # [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO
u'\u228A': u'\u228B', # [BEST FIT] SUBSET OF WITH NOT EQUAL TO
u'\u228B': u'\u228A', # [BEST FIT] SUPERSET OF WITH NOT EQUAL TO
u'\u228F': u'\u2290', # SQUARE IMAGE OF
u'\u2290': u'\u228F', # SQUARE ORIGINAL OF
u'\u2291': u'\u2292', # SQUARE IMAGE OF OR EQUAL TO
u'\u2292': u'\u2291', # SQUARE ORIGINAL OF OR EQUAL TO
u'\u2298': u'\u29B8', # CIRCLED DIVISION SLASH
u'\u22A2': u'\u22A3', # RIGHT TACK
u'\u22A3': u'\u22A2', # LEFT TACK
u'\u22A6': u'\u2ADE', # ASSERTION
u'\u22A8': u'\u2AE4', # TRUE
u'\u22A9': u'\u2AE3', # FORCES
u'\u22AB': u'\u2AE5', # DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
u'\u22B0': u'\u22B1', # PRECEDES UNDER RELATION
u'\u22B1': u'\u22B0', # SUCCEEDS UNDER RELATION
u'\u22B2': u'\u22B3', # NORMAL SUBGROUP OF
u'\u22B3': u'\u22B2', # CONTAINS AS NORMAL SUBGROUP
u'\u22B4': u'\u22B5', # NORMAL SUBGROUP OF OR EQUAL TO
u'\u22B5': u'\u22B4', # CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
u'\u22B6': u'\u22B7', # ORIGINAL OF
u'\u22B7': u'\u22B6', # IMAGE OF
u'\u22C9': u'\u22CA', # LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
u'\u22CA': u'\u22C9', # RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
u'\u22CB': u'\u22CC', # LEFT SEMIDIRECT PRODUCT
u'\u22CC': u'\u22CB', # RIGHT SEMIDIRECT PRODUCT
u'\u22CD': u'\u2243', # REVERSED TILDE EQUALS
u'\u22D0': u'\u22D1', # DOUBLE SUBSET
u'\u22D1': u'\u22D0', # DOUBLE SUPERSET
u'\u22D6': u'\u22D7', # LESS-THAN WITH DOT
u'\u22D7': u'\u22D6', # GREATER-THAN WITH DOT
u'\u22D8': u'\u22D9', # VERY MUCH LESS-THAN
u'\u22D9': u'\u22D8', # VERY MUCH GREATER-THAN
u'\u22DA': u'\u22DB', # LESS-THAN EQUAL TO OR GREATER-THAN
u'\u22DB': u'\u22DA', # GREATER-THAN EQUAL TO OR LESS-THAN
u'\u22DC': u'\u22DD', # EQUAL TO OR LESS-THAN
u'\u22DD': u'\u22DC', # EQUAL TO OR GREATER-THAN
u'\u22DE': u'\u22DF', # EQUAL TO OR PRECEDES
u'\u22DF': u'\u22DE', # EQUAL TO OR SUCCEEDS
u'\u22E0': u'\u22E1', # [BEST FIT] DOES NOT PRECEDE OR EQUAL
u'\u22E1': u'\u22E0', # [BEST FIT] DOES NOT SUCCEED OR EQUAL
u'\u22E2': u'\u22E3', # [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO
u'\u22E3': u'\u22E2', # [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO
u'\u22E4': u'\u22E5', # [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO
u'\u22E5': u'\u22E4', # [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO
u'\u22E6': u'\u22E7', # [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO
u'\u22E7': u'\u22E6', # [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO
u'\u22E8': u'\u22E9', # [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO
u'\u22E9': u'\u22E8', # [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO
u'\u22EA': u'\u22EB', # [BEST FIT] NOT NORMAL SUBGROUP OF
u'\u22EB': u'\u22EA', # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP
u'\u22EC': u'\u22ED', # [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO
# [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
u'\u22ED': u'\u22EC',
u'\u22F0': u'\u22F1', # UP RIGHT DIAGONAL ELLIPSIS
u'\u22F1': u'\u22F0', # DOWN RIGHT DIAGONAL ELLIPSIS
u'\u22F2': u'\u22FA', # ELEMENT OF WITH LONG HORIZONTAL STROKE
u'\u22F3': u'\u22FB', # ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
u'\u22F4': u'\u22FC', # SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
u'\u22F6': u'\u22FD', # ELEMENT OF WITH OVERBAR
u'\u22F7': u'\u22FE', # SMALL ELEMENT OF WITH OVERBAR
u'\u22FA': u'\u22F2', # CONTAINS WITH LONG HORIZONTAL STROKE
u'\u22FB': u'\u22F3', # CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
u'\u22FC': u'\u22F4', # SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
u'\u22FD': u'\u22F6', # CONTAINS WITH OVERBAR
u'\u22FE': u'\u22F7', # SMALL CONTAINS WITH OVERBAR
u'\u2308': u'\u2309', # LEFT CEILING
u'\u2309': u'\u2308', # RIGHT CEILING
u'\u230A': u'\u230B', # LEFT FLOOR
u'\u230B': u'\u230A', # RIGHT FLOOR
u'\u2329': u'\u232A', # LEFT-POINTING ANGLE BRACKET
u'\u232A': u'\u2329', # RIGHT-POINTING ANGLE BRACKET
u'\u2768': u'\u2769', # MEDIUM LEFT PARENTHESIS ORNAMENT
u'\u2769': u'\u2768', # MEDIUM RIGHT PARENTHESIS ORNAMENT
u'\u276A': u'\u276B', # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
u'\u276B': u'\u276A', # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
u'\u276C': u'\u276D', # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
u'\u276D': u'\u276C', # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
u'\u276E': u'\u276F', # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
u'\u276F': u'\u276E', # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
u'\u2770': u'\u2771', # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
u'\u2771': u'\u2770', # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
u'\u2772': u'\u2773', # LIGHT LEFT TORTOISE SHELL BRACKET
u'\u2773': u'\u2772', # LIGHT RIGHT TORTOISE SHELL BRACKET
u'\u2774': u'\u2775', # MEDIUM LEFT CURLY BRACKET ORNAMENT
u'\u2775': u'\u2774', # MEDIUM RIGHT CURLY BRACKET ORNAMENT
u'\u27C3': u'\u27C4', # OPEN SUBSET
u'\u27C4': u'\u27C3', # OPEN SUPERSET
u'\u27C5': u'\u27C6', # LEFT S-SHAPED BAG DELIMITER
u'\u27C6': u'\u27C5', # RIGHT S-SHAPED BAG DELIMITER
u'\u27C8': u'\u27C9', # REVERSE SOLIDUS PRECEDING SUBSET
u'\u27C9': u'\u27C8', # SUPERSET PRECEDING SOLIDUS
u'\u27D5': u'\u27D6', # LEFT OUTER JOIN
u'\u27D6': u'\u27D5', # RIGHT OUTER JOIN
u'\u27DD': u'\u27DE', # LONG RIGHT TACK
u'\u27DE': u'\u27DD', # LONG LEFT TACK
u'\u27E2': u'\u27E3', # WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK
u'\u27E3': u'\u27E2', # WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK
u'\u27E4': u'\u27E5', # WHITE SQUARE WITH LEFTWARDS TICK
u'\u27E5': u'\u27E4', # WHITE SQUARE WITH RIGHTWARDS TICK
u'\u27E6': u'\u27E7', # MATHEMATICAL LEFT WHITE SQUARE BRACKET
u'\u27E7': u'\u27E6', # MATHEMATICAL RIGHT WHITE SQUARE BRACKET
u'\u27E8': u'\u27E9', # MATHEMATICAL LEFT ANGLE BRACKET
u'\u27E9': u'\u27E8', # MATHEMATICAL RIGHT ANGLE BRACKET
u'\u27EA': u'\u27EB', # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
u'\u27EB': u'\u27EA', # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
u'\u27EC': u'\u27ED', # MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
u'\u27ED': u'\u27EC', # MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
u'\u27EE': u'\u27EF', # MATHEMATICAL LEFT FLATTENED PARENTHESIS
u'\u27EF': u'\u27EE', # MATHEMATICAL RIGHT FLATTENED PARENTHESIS
u'\u2983': u'\u2984', # LEFT WHITE CURLY BRACKET
u'\u2984': u'\u2983', # RIGHT WHITE CURLY BRACKET
u'\u2985': u'\u2986', # LEFT WHITE PARENTHESIS
u'\u2986': u'\u2985', # RIGHT WHITE PARENTHESIS
u'\u2987': u'\u2988', # Z NOTATION LEFT IMAGE BRACKET
u'\u2988': u'\u2987', # Z NOTATION RIGHT IMAGE BRACKET
u'\u2989': u'\u298A', # Z NOTATION LEFT BINDING BRACKET
u'\u298A': u'\u2989', # Z NOTATION RIGHT BINDING BRACKET
u'\u298B': u'\u298C', # LEFT SQUARE BRACKET WITH UNDERBAR
u'\u298C': u'\u298B', # RIGHT SQUARE BRACKET WITH UNDERBAR
u'\u298D': u'\u2990', # LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
u'\u298E': u'\u298F', # RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
u'\u298F': u'\u298E', # LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
u'\u2990': u'\u298D', # RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
u'\u2991': u'\u2992', # LEFT ANGLE BRACKET WITH DOT
u'\u2992': u'\u2991', # RIGHT ANGLE BRACKET WITH DOT
u'\u2993': u'\u2994', # LEFT ARC LESS-THAN BRACKET
u'\u2994': u'\u2993', # RIGHT ARC GREATER-THAN BRACKET
u'\u2995': u'\u2996', # DOUBLE LEFT ARC GREATER-THAN BRACKET
u'\u2996': u'\u2995', # DOUBLE RIGHT ARC LESS-THAN BRACKET
u'\u2997': u'\u2998', # LEFT BLACK TORTOISE SHELL BRACKET
u'\u2998': u'\u2997', # RIGHT BLACK TORTOISE SHELL BRACKET
u'\u29B8': u'\u2298', # CIRCLED REVERSE SOLIDUS
u'\u29C0': u'\u29C1', # CIRCLED LESS-THAN
u'\u29C1': u'\u29C0', # CIRCLED GREATER-THAN
u'\u29C4': u'\u29C5', # SQUARED RISING DIAGONAL SLASH
u'\u29C5': u'\u29C4', # SQUARED FALLING DIAGONAL SLASH
u'\u29CF': u'\u29D0', # LEFT TRIANGLE BESIDE VERTICAL BAR
u'\u29D0': u'\u29CF', # VERTICAL BAR BESIDE RIGHT TRIANGLE
u'\u29D1': u'\u29D2', # BOWTIE WITH LEFT HALF BLACK
u'\u29D2': u'\u29D1', # BOWTIE WITH RIGHT HALF BLACK
u'\u29D4': u'\u29D5', # TIMES WITH LEFT HALF BLACK
u'\u29D5': u'\u29D4', # TIMES WITH RIGHT HALF BLACK
u'\u29D8': u'\u29D9', # LEFT WIGGLY FENCE
u'\u29D9': u'\u29D8', # RIGHT WIGGLY FENCE
u'\u29DA': u'\u29DB', # LEFT DOUBLE WIGGLY FENCE
u'\u29DB': u'\u29DA', # RIGHT DOUBLE WIGGLY FENCE
u'\u29F5': u'\u2215', # REVERSE SOLIDUS OPERATOR
u'\u29F8': u'\u29F9', # BIG SOLIDUS
u'\u29F9': u'\u29F8', # BIG REVERSE SOLIDUS
u'\u29FC': u'\u29FD', # LEFT-POINTING CURVED ANGLE BRACKET
u'\u29FD': u'\u29FC', # RIGHT-POINTING CURVED ANGLE BRACKET
u'\u2A2B': u'\u2A2C', # MINUS SIGN WITH FALLING DOTS
u'\u2A2C': u'\u2A2B', # MINUS SIGN WITH RISING DOTS
u'\u2A2D': u'\u2A2E', # PLUS SIGN IN LEFT HALF CIRCLE
u'\u2A2E': u'\u2A2D', # PLUS SIGN IN RIGHT HALF CIRCLE
u'\u2A34': u'\u2A35', # MULTIPLICATION SIGN IN LEFT HALF CIRCLE
u'\u2A35': u'\u2A34', # MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
u'\u2A3C': u'\u2A3D', # INTERIOR PRODUCT
u'\u2A3D': u'\u2A3C', # RIGHTHAND INTERIOR PRODUCT
u'\u2A64': u'\u2A65', # Z NOTATION DOMAIN ANTIRESTRICTION
u'\u2A65': u'\u2A64', # Z NOTATION RANGE ANTIRESTRICTION
u'\u2A79': u'\u2A7A', # LESS-THAN WITH CIRCLE INSIDE
u'\u2A7A': u'\u2A79', # GREATER-THAN WITH CIRCLE INSIDE
u'\u2A7D': u'\u2A7E', # LESS-THAN OR SLANTED EQUAL TO
u'\u2A7E': u'\u2A7D', # GREATER-THAN OR SLANTED EQUAL TO
u'\u2A7F': u'\u2A80', # LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
u'\u2A80': u'\u2A7F', # GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
u'\u2A81': u'\u2A82', # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
u'\u2A82': u'\u2A81', # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
u'\u2A83': u'\u2A84', # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
u'\u2A84': u'\u2A83', # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
u'\u2A8B': u'\u2A8C', # LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
u'\u2A8C': u'\u2A8B', # GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
u'\u2A91': u'\u2A92', # LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
u'\u2A92': u'\u2A91', # GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
# LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
u'\u2A93': u'\u2A94',
# GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
u'\u2A94': u'\u2A93',
u'\u2A95': u'\u2A96', # SLANTED EQUAL TO OR LESS-THAN
u'\u2A96': u'\u2A95', # SLANTED EQUAL TO OR GREATER-THAN
u'\u2A97': u'\u2A98', # SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
u'\u2A98': u'\u2A97', # SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
u'\u2A99': u'\u2A9A', # DOUBLE-LINE EQUAL TO OR LESS-THAN
u'\u2A9A': u'\u2A99', # DOUBLE-LINE EQUAL TO OR GREATER-THAN
u'\u2A9B': u'\u2A9C', # DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN
u'\u2A9C': u'\u2A9B', # DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN
u'\u2AA1': u'\u2AA2', # DOUBLE NESTED LESS-THAN
u'\u2AA2': u'\u2AA1', # DOUBLE NESTED GREATER-THAN
u'\u2AA6': u'\u2AA7', # LESS-THAN CLOSED BY CURVE
u'\u2AA7': u'\u2AA6', # GREATER-THAN CLOSED BY CURVE
u'\u2AA8': u'\u2AA9', # LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
u'\u2AA9': u'\u2AA8', # GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
u'\u2AAA': u'\u2AAB', # SMALLER THAN
u'\u2AAB': u'\u2AAA', # LARGER THAN
u'\u2AAC': u'\u2AAD', # SMALLER THAN OR EQUAL TO
u'\u2AAD': u'\u2AAC', # LARGER THAN OR EQUAL TO
u'\u2AAF': u'\u2AB0', # PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
u'\u2AB0': u'\u2AAF', # SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
u'\u2AB3': u'\u2AB4', # PRECEDES ABOVE EQUALS SIGN
u'\u2AB4': u'\u2AB3', # SUCCEEDS ABOVE EQUALS SIGN
u'\u2ABB': u'\u2ABC', # DOUBLE PRECEDES
u'\u2ABC': u'\u2ABB', # DOUBLE SUCCEEDS
u'\u2ABD': u'\u2ABE', # SUBSET WITH DOT
u'\u2ABE': u'\u2ABD', # SUPERSET WITH DOT
u'\u2ABF': u'\u2AC0', # SUBSET WITH PLUS SIGN BELOW
u'\u2AC0': u'\u2ABF', # SUPERSET WITH PLUS SIGN BELOW
u'\u2AC1': u'\u2AC2', # SUBSET WITH MULTIPLICATION SIGN BELOW
u'\u2AC2': u'\u2AC1', # SUPERSET WITH MULTIPLICATION SIGN BELOW
u'\u2AC3': u'\u2AC4', # SUBSET OF OR EQUAL TO WITH DOT ABOVE
u'\u2AC4': u'\u2AC3', # SUPERSET OF OR EQUAL TO WITH DOT ABOVE
u'\u2AC5': u'\u2AC6', # SUBSET OF ABOVE EQUALS SIGN
u'\u2AC6': u'\u2AC5', # SUPERSET OF ABOVE EQUALS SIGN
u'\u2ACD': u'\u2ACE', # SQUARE LEFT OPEN BOX OPERATOR
u'\u2ACE': u'\u2ACD', # SQUARE RIGHT OPEN BOX OPERATOR
u'\u2ACF': u'\u2AD0', # CLOSED SUBSET
u'\u2AD0': u'\u2ACF', # CLOSED SUPERSET
u'\u2AD1': u'\u2AD2', # CLOSED SUBSET OR EQUAL TO
u'\u2AD2': u'\u2AD1', # CLOSED SUPERSET OR EQUAL TO
u'\u2AD3': u'\u2AD4', # SUBSET ABOVE SUPERSET
u'\u2AD4': u'\u2AD3', # SUPERSET ABOVE SUBSET
u'\u2AD5': u'\u2AD6', # SUBSET ABOVE SUBSET
u'\u2AD6': u'\u2AD5', # SUPERSET ABOVE SUPERSET
u'\u2ADE': u'\u22A6', # SHORT LEFT TACK
u'\u2AE3': u'\u22A9', # DOUBLE VERTICAL BAR LEFT TURNSTILE
u'\u2AE4': u'\u22A8', # VERTICAL BAR DOUBLE LEFT TURNSTILE
u'\u2AE5': u'\u22AB', # DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE
u'\u2AEC': u'\u2AED', # DOUBLE STROKE NOT SIGN
u'\u2AED': u'\u2AEC', # REVERSED DOUBLE STROKE NOT SIGN
u'\u2AF7': u'\u2AF8', # TRIPLE NESTED LESS-THAN
u'\u2AF8': u'\u2AF7', # TRIPLE NESTED GREATER-THAN
u'\u2AF9': u'\u2AFA', # DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO
u'\u2AFA': u'\u2AF9', # DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO
u'\u2E02': u'\u2E03', # LEFT SUBSTITUTION BRACKET
u'\u2E03': u'\u2E02', # RIGHT SUBSTITUTION BRACKET
u'\u2E04': u'\u2E05', # LEFT DOTTED SUBSTITUTION BRACKET
u'\u2E05': u'\u2E04', # RIGHT DOTTED SUBSTITUTION BRACKET
u'\u2E09': u'\u2E0A', # LEFT TRANSPOSITION BRACKET
u'\u2E0A': u'\u2E09', # RIGHT TRANSPOSITION BRACKET
u'\u2E0C': u'\u2E0D', # LEFT RAISED OMISSION BRACKET
u'\u2E0D': u'\u2E0C', # RIGHT RAISED OMISSION BRACKET
u'\u2E1C': u'\u2E1D', # LEFT LOW PARAPHRASE BRACKET
u'\u2E1D': u'\u2E1C', # RIGHT LOW PARAPHRASE BRACKET
u'\u2E20': u'\u2E21', # LEFT VERTICAL BAR WITH QUILL
u'\u2E21': u'\u2E20', # RIGHT VERTICAL BAR WITH QUILL
u'\u2E22': u'\u2E23', # TOP LEFT HALF BRACKET
u'\u2E23': u'\u2E22', # TOP RIGHT HALF BRACKET
u'\u2E24': u'\u2E25', # BOTTOM LEFT HALF BRACKET
u'\u2E25': u'\u2E24', # BOTTOM RIGHT HALF BRACKET
u'\u2E26': u'\u2E27', # LEFT SIDEWAYS U BRACKET
u'\u2E27': u'\u2E26', # RIGHT SIDEWAYS U BRACKET
u'\u2E28': u'\u2E29', # LEFT DOUBLE PARENTHESIS
u'\u2E29': u'\u2E28', # RIGHT DOUBLE PARENTHESIS
u'\u3008': u'\u3009', # LEFT ANGLE BRACKET
u'\u3009': u'\u3008', # RIGHT ANGLE BRACKET
u'\u300A': u'\u300B', # LEFT DOUBLE ANGLE BRACKET
u'\u300B': u'\u300A', # RIGHT DOUBLE ANGLE BRACKET
u'\u300C': u'\u300D', # [BEST FIT] LEFT CORNER BRACKET
u'\u300D': u'\u300C', # [BEST FIT] RIGHT CORNER BRACKET
u'\u300E': u'\u300F', # [BEST FIT] LEFT WHITE CORNER BRACKET
u'\u300F': u'\u300E', # [BEST FIT] RIGHT WHITE CORNER BRACKET
u'\u3010': u'\u3011', # LEFT BLACK LENTICULAR BRACKET
u'\u3011': u'\u3010', # RIGHT BLACK LENTICULAR BRACKET
u'\u3014': u'\u3015', # LEFT TORTOISE SHELL BRACKET
u'\u3015': u'\u3014', # RIGHT TORTOISE SHELL BRACKET
u'\u3016': u'\u3017', # LEFT WHITE LENTICULAR BRACKET
u'\u3017': u'\u3016', # RIGHT WHITE LENTICULAR BRACKET
u'\u3018': u'\u3019', # LEFT WHITE TORTOISE SHELL BRACKET
u'\u3019': u'\u3018', # RIGHT WHITE TORTOISE SHELL BRACKET
u'\u301A': u'\u301B', # LEFT WHITE SQUARE BRACKET
u'\u301B': u'\u301A', # RIGHT WHITE SQUARE BRACKET
u'\uFE59': u'\uFE5A', # SMALL LEFT PARENTHESIS
u'\uFE5A': u'\uFE59', # SMALL RIGHT PARENTHESIS
u'\uFE5B': u'\uFE5C', # SMALL LEFT CURLY BRACKET
u'\uFE5C': u'\uFE5B', # SMALL RIGHT CURLY BRACKET
u'\uFE5D': u'\uFE5E', # SMALL LEFT TORTOISE SHELL BRACKET
u'\uFE5E': u'\uFE5D', # SMALL RIGHT TORTOISE SHELL BRACKET
u'\uFE64': u'\uFE65', # SMALL LESS-THAN SIGN
u'\uFE65': u'\uFE64', # SMALL GREATER-THAN SIGN
u'\uFF08': u'\uFF09', # FULLWIDTH LEFT PARENTHESIS
u'\uFF09': u'\uFF08', # FULLWIDTH RIGHT PARENTHESIS
u'\uFF1C': u'\uFF1E', # FULLWIDTH LESS-THAN SIGN
u'\uFF1E': u'\uFF1C', # FULLWIDTH GREATER-THAN SIGN
u'\uFF3B': u'\uFF3D', # FULLWIDTH LEFT SQUARE BRACKET
u'\uFF3D': u'\uFF3B', # FULLWIDTH RIGHT SQUARE BRACKET
u'\uFF5B': u'\uFF5D', # FULLWIDTH LEFT CURLY BRACKET
u'\uFF5D': u'\uFF5B', # FULLWIDTH RIGHT CURLY BRACKET
u'\uFF5F': u'\uFF60', # FULLWIDTH LEFT WHITE PARENTHESIS
u'\uFF60': u'\uFF5F', # FULLWIDTH RIGHT WHITE PARENTHESIS
u'\uFF62': u'\uFF63', # [BEST FIT] HALFWIDTH LEFT CORNER BRACKET
u'\uFF63': u'\uFF62', # [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET
}

View File

@ -0,0 +1,251 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="custom_external_layout_standard_barcode">
<div class="header">
<div class="row">
<br/>
<table align="left"
style="border:1px solid white;width:50;margin-left:60px;margin-top:0px;margin-right:5px;margin-bottom:40px;text-align: justify;">
<tr style="border:0px">
<td style="font-weight: bold;border:0px;padding-right:30px !important;">Number:
<t t-esc="o.name"/>
</td>
</tr>
<tr style="border:0px">
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold;font-stretch: expanded;">
Date:
</span>
<t t-set="date_hijri" t-value="o.transaction_date_hijri"/>
<t t-set="date_hijri2" t-value="date_hijri.split('-')"/>
<t t-esc="date_hijri2[0]"/>/<t t-esc="date_hijri2[1]"/>/
<t t-esc="date_hijri2[2]"/>
</td>
</tr>
<tr>
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold;">Reference:</span>
<span t-field="o.transaction_date" t-field-options='{"format": "yyyy/MM/dd"}'/>
</td>
</tr>
<tr>
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold">Attachment:</span>
<span style="direction: ltr !important;">
<t t-esc="o.attachment_num"/>
</span>
</td>
</tr>
<tr style="border:0px">
<td colspan="2" style="border:0px">
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', o.name, 600, 100)"
style="width:250px;height:40px;margin-top:5px;"/>
</td>
</tr>
</table>
</div>
</div>
<div class="article o_report_layout_standard">
<t t-raw="0"/>
</div>
<div class="footer">
<!-- <div class="text-center">-->
<!-- <div class="text-muted">-->
<!-- Page: <span class="page"/> / <span class="topage"/>-->
<!-- </div>-->
<!-- </div>-->
</div>
</template>
<template id="barcode_external_layout">
<!-- Multicompany -->
<t t-if="not o and doc">
<t t-set="o" t-value="doc"/>
</t>
<t t-if="o and 'company_id' in o">
<t t-set="company" t-value="o.company_id.sudo()"/>
</t>
<t t-if="not o or not 'company_id' in o">
<t t-set="company" t-value="res_company"/>
</t>
<div class="header">
<div class="row">
<br/>
<table align="left"
style="border:1px solid white;width:50;margin-left:60px;margin-top:0px;margin-right:5px;margin-bottom:40px;text-align: justify;">
<tr style="border:0px">
<td style="font-weight: bold;border:0px;padding-right:30px !important;">Number:
<t t-esc="o.name"/>
</td>
</tr>
<tr style="border:0px">
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold;font-stretch: expanded;">
Date:
</span>
<t t-set="date_hijri" t-value="o.transaction_date_hijri"/>
<t t-set="date_hijri2" t-value="date_hijri.split('-')"/>
<t t-esc="date_hijri2[0]"/>/<t t-esc="date_hijri2[1]"/>/
<t t-esc="date_hijri2[2]"/>
</td>
</tr>
<tr>
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold;">Reference:</span>
<span t-field="o.transaction_date" t-field-options='{"format": "yyyy/MM/dd"}'/>
</td>
</tr>
<tr>
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold">Attachment:</span>
<span style="direction: ltr !important;">
<t t-esc="o.attachment_num"/>
</span>
</td>
</tr>
<tr style="border:0px">
<td colspan="2" style="border:0px">
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', o.name, 600, 100)"
style="width:250px;height:40px;margin-top:5px;"/>
</td>
</tr>
</table>
</div>
</div>
<br></br>
<!-- <t t-if="company.external_report_layout == 'background'">-->
<!-- <t t-call="cm_odex_barcode.custom_external_layout_standard_barcode"><t t-raw="0"/></t>-->
<!-- </t>-->
<!-- <t t-if="company.external_report_layout == 'boxed'">-->
<!-- <t t-call="cm_odex_barcode.custom_external_layout_standard_barcode"><t t-raw="0"/></t>-->
<!-- </t>-->
<!-- <t t-if="company.external_report_layout == 'clean'">-->
<!-- <t t-call="cm_odex_barcode.custom_external_layout_standard_barcode"><t t-raw="0"/></t>-->
<!-- </t>-->
<!-- <t t-if="company.external_report_layout in (False, 'standard')">-->
<!-- <t t-call="cm_odex_barcode.custom_external_layout_standard_barcode"><t t-raw="0"/></t>-->
<!-- </t>-->
</template>
<!-- Translatable template -->
<template id="report_transaction_barcode">
<t t-call="cm_odex_barcode.barcode_external_layout">
<div class="page">
<div class="row text-center" style="direction: rtl;">
<table style="margin-right:70%;">
<tr>
<td style="font-weight: bold;">Number:</td>
<td style="direction: ltr !important;">
<t t-esc="o.name"/>
</td>
</tr>
</table>
<table style="margin-right:70%;">
<tr>
<td><span style="font-weight:bold">Date:</span>
<t t-set="date_hijri" t-value="o.transaction_date_hijri"/>
<t t-set="date_hijri2" t-value="date_hijri.split('-')"/>
<t t-esc="date_hijri2[0]"/>/<t t-esc="date_hijri2[1]"/>/<t t-esc="date_hijri2[2]"/>
</td>
</tr>
<tr>
<td><span style="font-weight:bold">Reference:</span>
<span t-field="o.transaction_date" t-field-options='{"format": "yyyy/MM/dd"}'/>
</td>
</tr>
<tr>
<td>
<span style="font-weight:bold">Attachment:</span>
<span style="direction: ltr !important;">
<t t-esc="o.attachment_num"/>
</span>
</td>
</tr>
<tr>
<td colspan="2">
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', o.name, 600, 100)"
style="width:250px;height:40px;margin-top:5px;"/>
</td>
</tr>
</table>
</div>
</div>
</t>
</template>
<template id="report_internal_transaction_barcode">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="cm_odex_barcode.report_transaction_barcode"/>
</t>
</t>
</template>
<template id="report_incoming_transaction_barcode">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="cm_odex_barcode.report_transaction_barcode"/>
</t>
</t>
</template>
<template id="report_outgoing_transaction_barcode">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="cm_odex_barcode.report_transaction_barcode"/>
</t>
</t>
</template>
<record id="paperformat_transactions" model="report.paperformat">
<field name="name">European A4 Transactions</field>
<field name="default" eval="True"/>
<field name="format">A4</field>
<field name="orientation">Portrait</field>
<field name="margin_top">5</field>
<field name="margin_bottom">28</field>
<field name="margin_left">7</field>
<field name="margin_right">7</field>
<field name="header_line" eval="False"/>
<field name="header_spacing">35</field>
<field name="dpi">90</field>
</record>
<record id="act_transaction_out_barcode" model="ir.actions.report">
<field name="name">Outgoing Transaction Barcode</field>
<field name="model">outgoing.transaction</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">cm_odex_barcode.report_outgoing_transaction_barcode</field>
<field name="attachment_use" eval="False"/>
<field name="paperformat_id" ref="paperformat_transactions"/>
</record>
<record id="act_transaction_in_barcode" model="ir.actions.report">
<field name="name">Incoming Transaction Barcode</field>
<field name="model">incoming.transaction</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">cm_odex_barcode.report_incoming_transaction_barcode</field>
<field name="attachment_use" eval="False"/>
<field name="paperformat_id" ref="paperformat_transactions"/>
</record>
<record id="act_transaction_internal_barcode" model="ir.actions.report">
<field name="name">Internal Transaction Barcode</field>
<field name="model">internal.transaction</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">cm_odex_barcode.report_internal_transaction_barcode</field>
<field name="attachment_use" eval="False"/>
<field name="paperformat_id" ref="paperformat_transactions"/>
</record>
</data>
</odoo>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="extend_tran_header" inherit_id="exp_transaction_documents.custom_external_layout_standard_tran">
<xpath expr="//div[@class='header']" position="replace">
<div class="header">
<div class="row">
<br/>
<table align="left"
style="border:1px solid white;width:50;margin-left:60px;margin-top:0px;margin-right:5px;margin-bottom:40px;text-align: justify;">
<tr style="border:0px">
<td style="font-weight: bold;border:0px;padding-right:30px !important;">Number:
<t t-esc="o.name"/>
</td>
</tr>
<tr style="border:0px">
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold;font-stretch: expanded;">
Date:
</span>
<t t-set="date_hijri" t-value="o.transaction_date_hijri"/>
<t t-set="date_hijri2" t-value="date_hijri.split('-')"/>
<t t-esc="date_hijri2[0]"/>/<t t-esc="date_hijri2[1]"/>/
<t t-esc="date_hijri2[2]"/>
</td>
</tr>
<tr>
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold;">Reference:</span>
<span t-field="o.transaction_date" t-field-options='{"format": "yyyy/MM/dd"}'/>
</td>
</tr>
<tr>
<td colspan="2" style="border:0px;padding-right:30px !important;">
<span style="font-weight:bold">Attachment:</span>
<span style="direction: ltr !important;">
<t t-esc="o.attachment_num"/>
</span>
</td>
</tr>
<tr style="border:0px">
<td colspan="2" style="border:0px">
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', o.name, 600, 100)"
style="width:250px;height:40px;margin-top:5px;"/>
</td>
</tr>
</table>
</div>
</div>
<br></br>
</xpath>
</template>
</data>
</odoo>

View File

@ -0,0 +1,51 @@
odoo.cm_odex_barcode = function (instance) {
'use strict';
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
console.log(">>>>>>>>>>>>>>>>>>>>>>>",QWeb)
instance.web.form.FieldBinaryImage.include({
render_value: function () {
var self = this;
var url;
if (this.get('value') && !instance.web.form.is_bin_size(this.get('value'))) {
url = 'data:image/png;base64,' + this.get('value');
} else if (this.get('value')) {
var id = JSON.stringify(this.view.datarecord.id || null);
var field = this.name;
if (this.options.preview_image)
field = this.options.preview_image;
url = this.session.url('/web/binary/image', {
model: this.view.dataset.model,
id: id,
field: field,
t: (new Date().getTime()),
});
} else {
url = this.placeholder;
}
var $img = $(QWeb.render("FieldBinaryImage-img", {widget: this, url: url}));
$($img).click(function (e) {
if (self.view.get("actual_mode") == "view") {
var $button = $(".oe_form_button_edit");
console.log($img[0].src)
window.location.href=$img[0].src
e.stopPropagation();
}
});
this.$el.find('> img').remove();
this.$el.prepend($img);
$img.load(function () {
if (!self.options.size)
return;
$img.css("max-width", "" + self.options.size[0] + "px");
$img.css("max-height", "" + self.options.size[1] + "px");
});
$img.on('error', function () {
self.on_clear();
$img.attr('src', self.placeholder);
instance.webclient.notification.warn(_t("Image"), _t("Could not display the selected image."));
});
},
});
};

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Flavio Corpa <flavio.corpa@tecnativa.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo>
<data>
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr=".">
<script type="text/javascript"
src="/cm_odex_barcode/static/src/js/cm_odex_barcode.js"/>
</xpath>
</template>
</data>
</odoo>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="form_internal_barcode_print_button" model="ir.ui.view">
<field name="name">print internal barcode</field>
<field name="model">internal.transaction</field>
<field name="inherit_id" ref="exp_transaction_documents.internal_transaction_form"/>
<field name="arch" type="xml">
<!-- <xpath expr="//header" position="inside">-->
<!-- <button type="action" name="%(act_transaction_internal_barcode)d" string="Print Barcode" class="oe_highlight"/>-->
<!-- </xpath>-->
<field name="type" position="after">
<field name="create_uid" invisible="1"/>
</field>
<xpath expr="/form/sheet/notebook/page[3]" position="after">
<page string="Review Barcode">
<group>
<field name="binary_barcode" widget="image"/>
</group>
</page>
</xpath>
</field>
</record>
<!---->
<record id="form_outgoing_barcode_print_button" model="ir.ui.view">
<field name="name">print barcode</field>
<field name="model">outgoing.transaction</field>
<field name="inherit_id" ref="exp_transaction_documents.outgoing_external_transaction_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button type="action" name="%(act_transaction_out_barcode)d" string="Print Barcode" class="oe_highlight"/>
</xpath>
<field name="type" position="after">
<field name="create_uid" invisible="1"/>
</field>
<xpath expr="/form/sheet/notebook/page[3]" position="after">
<page string="Review Barcode">
<group>
<field name="binary_barcode" widget="image"/>
</group>
</page>
</xpath>
</field>
</record>
<!---->
<record id="form_incoming_barcode_print_button" model="ir.ui.view">
<field name="name">print incoming barcode</field>
<field name="model">incoming.transaction</field>
<field name="inherit_id" ref="exp_transaction_documents.incoming_external_transaction_form"/>
<field name="arch" type="xml">
<!-- <xpath expr="//header" position="inside">-->
<!-- <button type="action" name="%(act_transaction_in_barcode)d" string="Print Barcode" class="oe_highlight"/>-->
<!-- </xpath>-->
<field name="type" position="after">
<field name="create_uid" invisible="1"/>
</field>
<xpath expr="/form/sheet/notebook/page[3]" position="after">
<page string="Review Barcode">
<group>
<field name="binary_barcode" widget="image"/>
</group>
</page>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,2 @@
#-*- coding: utf-8 -*-
from . import models

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odex - Communications Management System.
# Copyright (C) 2017 Expert Co. Ltd. (<http://exp-sa.com>).
#
##############################################################################
{
'name': 'Correspondence Traking (Mailing)',
'version': '0.1',
'sequence': 20,
'author': 'Expert Co. Ltd.',
'category': 'Odex25-Transactions/Odex25-Transactions',
'summary': 'Mailing <> Correspondence Traking',
'description': """
Odex - Communications Management System - Link with Email
==========================================================
* Import Transactions from email.
""",
'website': 'http://www.exp-sa.com',
'depends': ['base', 'base_odex', 'mail','exp_transaction_documents'],
'data': [
'views/actions_and_menus.xml',
],
'qweb': [
],
'external_dependencies': {},
'installable': True,
'auto_install': False,
'application': False,
}

View File

@ -0,0 +1,98 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * exp_cm_mail_odex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-09-22 13:13+0000\n"
"PO-Revision-Date: 2019-09-22 13:13+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: exp_cm_mail_odex
#: model:ir.model.fields,help:exp_cm_mail_odex.field_cm_subject_type_default_value_email
#: model:ir.model.fields,help:exp_cm_mail_odex.field_cm_transaction_important_default_value_email
msgid "Check if you will used in email."
msgstr "حدد إذا كانت القيمه الافتراضية المستخدمه في معاملات البريد الالكتروني"
#. module: exp_cm_mail_odex
#: model:ir.actions.act_window,help:exp_cm_mail_odex.cm_transaction_internal_in_mail__list_action
msgid "Create the first Internal Transaction"
msgstr "إنشاء معامله داخلية جديده"
#. module: exp_cm_mail_odex
#: model:ir.model.fields,field_description:exp_cm_mail_odex.field_cm_subject_type_default_value_email
#: model:ir.model.fields,field_description:exp_cm_mail_odex.field_cm_transaction_important_default_value_email
msgid "Default value in email"
msgstr "القيمة الافتراضية للبريد الالكتروني"
#. module: exp_cm_mail_odex
#: code:addons/exp_cm_mail_odex/models/extend_transaction.py:11
#: selection:incoming.transaction,source:0
#: selection:internal.transaction,source:0
#: selection:outgoing.transaction,source:0
#: selection:transaction.transaction,source:0
#, python-format
msgid "Email"
msgstr "البريد الالكتروني"
#. module: exp_cm_mail_odex
#: model:ir.actions.act_window,name:exp_cm_mail_odex.cm_transaction_internal_in_mail__list_action
#: model:ir.ui.menu,name:exp_cm_mail_odex.cm_transaction_internal_in_menu
msgid "Email Internal Transactions"
msgstr "معاملات البريد الالكتروني"
#. module: exp_cm_mail_odex
#: code:addons/exp_cm_mail_odex/models/extend_transaction.py:10
#: selection:incoming.transaction,source:0
#: selection:internal.transaction,source:0
#: selection:outgoing.transaction,source:0
#: selection:transaction.transaction,source:0
#, python-format
msgid "Manual"
msgstr "يدوي"
#. module: exp_cm_mail_odex
#: code:addons/exp_cm_mail_odex/models/extend_transaction.py:12
#: selection:incoming.transaction,source:0
#: selection:internal.transaction,source:0
#: selection:outgoing.transaction,source:0
#: selection:transaction.transaction,source:0
#, python-format
msgid "System"
msgstr "تلقائي"
#. module: exp_cm_mail_odex
#: model:ir.model.fields,field_description:exp_cm_mail_odex.field_incoming_transaction_source
#: model:ir.model.fields,field_description:exp_cm_mail_odex.field_internal_transaction_source
#: model:ir.model.fields,field_description:exp_cm_mail_odex.field_outgoing_transaction_source
#: model:ir.model.fields,field_description:exp_cm_mail_odex.field_transaction_transaction_source
msgid "Transaction Source"
msgstr "مصدر المعامله"
#. module: exp_cm_mail_odex
#: model:ir.model,name:exp_cm_mail_odex.model_cm_subject_type
msgid "cm.subject.type"
msgstr "cm.subject.type"
#. module: exp_cm_mail_odex
#: model:ir.model,name:exp_cm_mail_odex.model_cm_transaction_important
msgid "cm.transaction.important"
msgstr "cm.transaction.important"
#. module: exp_cm_mail_odex
#: model:ir.model,name:exp_cm_mail_odex.model_transaction_transaction
msgid "for common attribute between transaction"
msgstr "for common attribute between transaction"
#. module: exp_cm_mail_odex
#: model:ir.model,name:exp_cm_mail_odex.model_internal_transaction
msgid "internal Transaction"
msgstr "المعاملات الداخلية"

View File

@ -0,0 +1,3 @@
#-*- coding: utf-8 -*-
from . import extend_related
from . import extend_transaction

View File

@ -0,0 +1,14 @@
#-*- coding: utf-8 -*-
from odoo import models, fields
class SubjectType(models.Model):
_inherit = 'cm.subject.type'
default_value_email = fields.Boolean(string='Default value in email', help='Check if you will used in email.', default=False)
class ImportantDegree(models.Model):
_inherit = 'cm.transaction.important'
default_value_email = fields.Boolean(string='Default value in email', help='Check if you will used in email.', default=False)

View File

@ -0,0 +1,83 @@
#-*- coding: utf-8 -*-
from odoo import api, models, fields,_
from email import utils
class Transaction(models.Model):
_inherit = 'transaction.transaction'
source = fields.Selection(string='Transaction Source', selection=[
('manual', _('Manual')),
('email', _('Email')),
('auto', _('System')),
], default='manual')
@api.returns('cm.entity')
def get_entity_by_email(self, emailaddr):
"""
return cm.entity using email address
:param emailaddr: entity email
:return: cm.entity: object
"""
Entity = self.env['cm.entity']
Partner = self.env['res.partner']
User = self.env['res.users']
email = utils.parseaddr(emailaddr)[1]
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>email",email)
partners = Partner.search([('email', '=ilike', email)])
# return first entity in this range
if not len(partners):
return Entity # empty entity
users = User.search([('partner_id', 'in', partners.ids)])
entities = Entity.search([('partner_id', 'in', partners.ids)])
if not len(entities) and len(users):
entities = Entity.search([('user_id', 'in', users.ids)])
return len(entities) and entities[0] or Entity
class IncomingTransaction(models.Model):
_inherit = 'internal.transaction'
@api.model
def message_new(self, msg_dict, custom_values=None):
"""
Overrides mail_thread message_new that is called by the mailgateway through message_process.
This override updates the document according to the email.
:param msg_dict: a map containing the email details and
attachments. See ``message_process`` and
``mail.message.parse`` for details.
:param custom_values: optional dictionary of additional
field values to pass to create()
when creating the new thread record.
Be careful, these values may override
any other values coming from the message.
:return: the id of the newly created thread object
"""
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>hello iam custom")
context = self.env.context
data = {}
if isinstance(custom_values, dict):
data = custom_values.copy()
model = context.get('thread_model') or self._name
model_pool = self.env[model]
fields = model_pool.with_context(**context).fields_get()
if 'subject' in fields and not data.get('name'):
data['subject'] = msg_dict.get('subject', '')
if 'body' in fields and not data.get('name'):
data['body'] = msg_dict.get('body', '')
email_from = msg_dict.get('email_from', '')
if email_from:
entity = self.get_entity_by_email(email_from)
if len(entity):
# data['to_ids'] = [(4, entity.id)]
data['employee_id'] = entity.id
data['state'] = 'draft'
data['source'] = 'email'
res_id = model_pool.with_context(**context).create(data)
res_id.fetch_sequence(data={}, now=True)
return res_id.id

View File

@ -0,0 +1,32 @@
<odoo>
<data>
<record model="ir.actions.act_window" id="cm_transaction_internal_in_mail__list_action">
<field name="name">Email Internal Transactions</field>
<field name="res_model">internal.transaction</field>
<field name="domain">[('source','=','email'),('employee_id.user_id', '=', uid),('state', 'not in', ['closed'])]</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Create the first Internal Transaction
</p>
</field>
<field name="search_view_id" ref="exp_transaction_documents.view_internal_transaction_filter"/>
<field name="context">{'search_default_favorite': 1,'search_default_unread': 1}</field>
</record>
<menuitem sequence="8" id="cm_transaction_internal_in_menu" name="Email Internal Transactions"
parent="exp_transaction_documents.parent_internal_tran_menu" action="cm_transaction_internal_in_mail__list_action"/>
<record id="cm_subject_type_inherit_form" model="ir.ui.view">
<field name="name">cm.subject.type.form</field>
<field name="model">cm.subject.type</field>
<field name="inherit_id" ref="exp_transaction_documents.cm_subject_type_form" />
<field name="arch" type="xml">
<field name="name" position="after">
<field name="default_value_email"/>
</field>
</field>
</record>
</data>
</odoo>

View File

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

View File

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odex - Communications Management System.
# Copyright (C) 2019 Expert Co. Ltd. (<http://exp-sa.com>).
#
##############################################################################
{
'name': 'Late Mail Reminder',
'version': '1.0',
'sequence': 10,
'author': "Expert Co. Ltd. - Sudan team",
'category': 'Odex25-Transactions/Odex25-Transactions',
'summary': 'mail reminder',
'description': """
Late Mail Reminder
========================================
extend for stander landed costs to change work flow and to add new field""",
'website': 'http://www.exp-sa.com',
'depends': ['fetchmail'],
'data': [
'data/late_email_data.xml',
'data/mail_data.xml',
'views/late_mail_reminder_views.xml',
'views/assets.xml',
],
'qweb' : [
# 'static/src/xml/notification_dialog.xml',
],
'installable': True,
'auto_install': False,
'application': True,
}

View File

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

View File

@ -0,0 +1,44 @@
# -*- encoding: utf-8 -*-
from odoo import http
from odoo.http import request
from odoo import SUPERUSER_ID
class ReminderPopupAlert(http.Controller):
@http.route('/reminder/notifications', type='json', auth="user")
def load_messages_alert(self, **kw):
cr, uid = request.cr, SUPERUSER_ID
cr.execute(
"""select mail_message_id from mail_message_res_partner_needaction_rel where res_partner_id = """ + str(
request.env.user.partner_id.id))
Messages_ids = []
for line in cr.fetchall():
s = request.env['mail.message'].browse(line[0])
for li in s:
for x in li.notification_ids:
if x.res_partner_id.id == request.env.user.partner_id.id and x.is_read == False:
Messages_ids.append(line[0])
list_item = []
Messages = http.request.env['mail.message'].search([('id', 'in', Messages_ids)], order='date asc')
base_url = http.request.env['ir.config_parameter'].sudo().get_param('web.base.url')
for message in Messages:
action_id = ''
if message.model:
reminder_line_ids = http.request.env['late.email.reminder.line'].sudo().search(
[('model_id', '=', message.model)])
# for reminder_line_id in reminder_line_ids:
# for group in reminder_line_id.group_ids:
# if request.env.user.id in group.users.ids:
# # if reminder_line_id.menu_id.action:
# action_id = reminder_line_id.menu_id.action.id
item = {
'subject': message.body,
'id': message.id,
'res_id': message.res_id,
'res_model': message.model,
'base_url': base_url,
# 'action_id' : action_id,
}
list_item.append(item)
return list_item

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- late email -->
<record id="exp_late_email_reminder" model="late.email.reminder">
<field name="number_of_days">5</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,15 @@
<odoo>
<data>
<record model="ir.cron" id="late_reminder_mail">
<field name="name">Late Email Reminder</field>
<field name="active">True</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_late_email_reminder" />
<field name="state">code</field>
<field name="code">model.action_send_late_email()</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import late_mail_remainder

View File

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
from datetime import date, datetime, timedelta
import logging
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATE_FORMAT
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DATE
from odoo import models, fields, api, _
_logger = logging.getLogger(__name__)
class LateEmailReminder(models.Model):
_name = 'late.email.reminder'
_order = 'create_date desc'
number_of_days = fields.Integer(string='Number Of Days')
line_ids = fields.One2many('late.email.reminder.line', 'late_email_id', string='Lines')
def execute(self):
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
def send_message(self, template=None, rec=None, model_name='', email='', partner_ids=[]):
if not template:
return
if not template:
return
template.write({'email_to': email,
})
mail_id = template.with_context(lang=self.env.user.lang).send_mail(
rec.id, force_send=True, raise_exception=False)
email = self.env['mail.mail'].browse(mail_id)
if email:
email.mail_message_id.partner_ids = [(6, 0, partner_ids)]
email.mail_message_id.needaction_partner_ids = [(6, 0, partner_ids)]
return True
def get_user_email(self, line):
# for line in line_ids:
user_ids = self.env['res.groups'].search([('id', 'in', line.group_ids.ids)])
user_email = []
partner_ids = []
for user in user_ids:
for rec in user.users:
partner_ids.append(rec.partner_id.id)
user_email.append(rec.partner_id.email)
user_email = list(set(user_email))
emails = ''
for email in user_email:
emails += email + ','
return emails,partner_ids
def action_send_late_email(self):
late_record = self.env['late.email.reminder'].search([], limit=1)
if late_record.line_ids:
states = ['cancel']
for line in late_record.line_ids:
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
user_email,partner_ids = self.get_user_email(line)
for state in line.record_state_ids:
states.append(state.name)
model_name = line.model_id.model
record = self.env[model_name].search([('state', 'not in', states)])
for rec in record:
order_date = rec.mapped(line.order_date.name)
last_date = ''
if order_date[0]:
if len(order_date[0]) > 10:
last_date = datetime.strptime(order_date[0], DATE_FORMAT).date()
else:
last_date = datetime.strptime(order_date[0], DATE).date()
end_date = last_date + timedelta(days=self.number_of_days)
date_now = date.today()
if end_date < date_now:
mail_id = self.send_message(line.template_id, rec, model_name, user_email,partner_ids)
class LateEmailReminderLine(models.Model):
_name = "late.email.reminder.line"
model_id = fields.Many2one('ir.model', string='Model')
order_date = fields.Many2one('ir.model.fields', string='Order Date')
record_state_ids = fields.Many2many('late.email.state', string='States')
template_id = fields.Many2one('mail.template', string='Template')
late_email_id = fields.Many2one('late.email.reminder', string='Late Email')
group_ids = fields.Many2many('res.groups', string='Groups')
@api.onchange('model_id')
def onchange_model_id(self):
domain = {}
self.order_date = False
domain = {'order_date': [('id', 'in', self.env['ir.model.fields'].search([('model_id', '=', self.model_id.id),
'|', ('ttype', '=', 'date'),
('ttype', '=', 'datetime')]).ids)],
'template_id': [('id', 'in', self.env['mail.template'].search([('model_id', '=', self.model_id.id)]).ids)]
}
return {'domain': domain}
class LateEmailState(models.Model):
_name = 'late.email.state'
name = fields.Char(string='name of state')

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,247 @@
.circle{
position: fixed;
top: 5.5%;
left: 1%;
z-index:99;
height: 30%;
width: 20%;
color:black;
}
.footer{
color: black;
margin-top: 400;
background-color: white;
}
.button_div{
background: #1c9980;
width : 50px;
height: 50px;
color:white;
}
.notifier-container{
background: #fff;
height: 100%;
width: 400px;
position: absolute;
top: 0;
right: 0;
box-shadow: 0 8px 14px 0 rgba(0, 0, 0, 0.38);
border-left: 1px solid #eee;
z-index: 100;
transform: translateX(400px);
transition: all .5s ease-in-out;
overflow: scroll;
}
.notifier-container h3{
border-bottom: 1px solid #1c9980;
padding: 20px 10px 5px 10px;
}
.notifier-container .notifier-body{
padding: 0 10px;
}
.notifier-container .notifier-body .alert{
margin-bottom: 5px;
background-color: #3E5D7F;
color: white;
}
.notifier-container .notifier-body ul{
padding: 0;
}
.notifier-container .notifier-body ul li a:nth-child(1){
color: #000;
font-weight: bold;
}
.notifier-container .notifier-body ul li a:nth-child(2){
float: right;
}
.notifier-overlayer{
display: none;
background: #00000061;
height: 100vh;
width: 100%;
position: absolute;
top: 0;
right: 0;
z-index: 99;
}
.notifier-button{
position: fixed;
text-align: center;
width: 100%;
bottom: -100px;
right: 45%;
z-index: 99;
transition: all .5s ease-in-out;
}
.containers {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
width:210px;
margin-right: 0px;
}
.rectangle {
/* display: flex;
text-align: center; */
justify-content: flex-start;
/* position: relative; */
width: 50px;
height: 55px;
background: #3E5D7F;
-webkit-transform: scale(0);
transform: scale(0);
border-radius: 50%;
color: white;
opacity: 0;
overflow: hidden;
cursor: pointer;
}
.rectangle.animate{
-webkit-animation: scale-in .4s ease-out forwards, expand .45s .35s ease-out forwards;
animation: scale-in .4s ease-out forwards, expand .45s .35s ease-out forwards;
}
.rectangle.animate-out{
-webkit-animation: scale-out .3s ease-in forwards, expand-out .35s .25s ease-in forwards;
animation: scale-out .3s ease-in forwards, expand-out .35s .25s ease-in forwards;
}
.notification-text .badge{
background-color: #f51212;
font-size: 14px;
margin: 5px;
margin-top: 10px;
}
.notification-text {
display: flex;
align-items: center;
padding: 0 14px;
font-family: 'Arial', sans-serif;
font-size: 14px;
margin: 5px;
margin-top: 10px;
}
.notification-text.animate{
-webkit-animation: fade-in .65s ease-in forwards;
animation: fade-in .65s ease-in forwards;
}
.notification-text.animate-out{
-webkit-animation: fade-out .65s ease-out backwards;
animation: fade-out .65s ease-out backwards;
}
@-webkit-keyframes scale-in {
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}
@keyframes scale-in {
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}
@-webkit-keyframes expand {
50% {
width: 350px;
border-radius: 6px;
}
100% {
width: 300px;
border-radius: 4px;
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 3px 3px -1px rgba(0, 0, 0, 0.12);
}
}
@keyframes expand {
50% {
width: 350px;
border-radius: 6px;
}
100% {
width: 300px;
border-radius: 4px;
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 3px 3px -1px rgba(0, 0, 0, 0.12);
}
}
@-webkit-keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: .8;
}
}
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: .8;
}
}
@-webkit-keyframes scale-out {
100% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0;
}
}
@keyframes scale-out {
100% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0;
}
}
@-webkit-keyframes expand-out {
50% {
width: 350px;
border-radius: 6px;
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 3px 3px -1px rgba(0, 0, 0, 0.12);
}
100% {
width: 50px;
border-radius: 50%;
box-shadow: none;
}
}
@keyframes expand-out {
50% {
width: 350px;
border-radius: 6px;
box-shadow: none;
}
100% {
width: 50px;
border-radius: 50%;
}
}
@-webkit-keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@ -0,0 +1,147 @@
odoo.define('exp_late_mail_reminder.notification_popup', function (require) {
"use strict";
//this file migrate from odoo9 to 11 written by altaher migrate by fatima 18/5/2020
var core = require('web.core');
var ajax = require('web.ajax');
var QWeb = core.qweb;
var rpc = require('web.rpc');
var ActionManager = require('web.ActionManager');
var Widget = require('web.Widget');
var ControlPanelMixin = require('web.ControlPanelMixin');
//
function save_hashing(url) {
var _id = '';
var p1 = /&action=\d+/g;
var record_id = url.match(p1);
if (record_id) {
_id = record_id[0].match(/\d+\d*/g)[0];
try {
var h = $.md5(_id.toString(), (new Date()).getFullYear().toString());
localStorage.setItem("navigator_token", h);
localStorage.setItem("navigator_token_act", _id);
}
catch (err) {
}
}
}
function save_hashing_record(url) {
var rec = '';
try {
var p1 = /#id=\d+/g;
var record_id = url.match(p1);
if (record_id) {
rec = record_id[0].match(/\d+\d*/g)[0];
var h = $.md5((new Date()).getFullYear().toString(), rec.toString());
localStorage.setItem("record_token", h);
localStorage.setItem("origin_record_id", rec);
}
}
catch (err) {
}
}
$(document).ready(function () {
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
/* if present, the header is where you move the DIV from:*/
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
/* otherwise, move the DIV from anywhere inside the DIV:*/
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
document.onmouseup = null;
document.onmousemove = null;
}
}
function load_message_loader() {
return ajax.jsonRpc('/reminder/notifications', 'call', {})
.then(function (results) {
if (results.length > 0) {
var content = '';
var no_message = results.length
$('body').append('<div class="notifier-container"><h2 style="color:#204060;">التنبيهات</h2><div class="col-md-12 notifier-body"></div></div>');
$('body').append('<div class="notifier-overlayer"></div>');
$('body').append('<div class="notifier-button style="dir:ltr"><div class="containers"><div class="rectangle"><div class="notification-text"><span><strong>لديك رسائل جديدة</span><span class="badge" id="badge">'+ no_message+'</span></strong></div></div></div></div>');
_.each(results, function (item) {
$('.notifier-body').append('<div data-id='+ item['id']+' class="alert alert-warning"><ul class="list-unstyled"><li><a href="'+item['base_url']+'/web#id='+ item['res_id']+'&view_type=form&model='+item['res_model']+'&action='+item['action_id'] +'">'+'<p style="color : white;"> '+item['subject'] +'</p></a></li></ul></div>');
});
var alert = document.querySelectorAll('.alert')
$(alert).click(function (event) {
var id = $(event.currentTarget).data('id');
var MessageModel = rpc.query({
model: 'mail.message',
method: 'mark_all_as_read',
args:[[],[['id','=',id]]]
}).then(
function () {
$(event.currentTarget).hide();
no_message = no_message - 1;
$('#badge').text(no_message);
$(".notifier-overlayer").fadeOut();
$(".notifier-container").css({"transform":"translateX(400px)"});
if(no_message > 0){
$(".rectangle-text").addClass("animate");
$(".rectangle").addClass("animate");
$(".notifier-button").css({"bottom":"20px"});
}
}
);
console.log(id);
});
$(".rectangle").click(function(){
$(".notifier-overlayer").show();
$(".notifier-container").css({"transform":"translateX(0)"});
$(".notifier-button").css({"bottom":"-100px"});
$(".rectangle-text").removeClass("animate");
$(".rectangle").removeClass("animate");
});
$(".notifier-overlayer").click(function(){
$(".notifier-overlayer").fadeOut();
$(".notifier-container").css({"transform":"translateX(400px)"});
$(".rectangle-text").addClass("animate");
$(".rectangle").addClass("animate");
$(".notifier-button").css({"bottom":"20px"});
});
$(".rectangle-text").addClass("animate");
$(".rectangle").addClass("animate");
$(".notifier-button").css({"bottom":"20px"});
}
});
}
setTimeout(load_message_loader, 2000);
});
});

View File

@ -0,0 +1,28 @@
<templates xml:space="preserve">
<t t-name="notification_template">
<!-- <div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix ui-draggable-handle">
رسائل جديدة (<t t-esc="message_no" />)
</div>
<table class="o_list_view table table-condensed table-striped" id="messages_table">
<thead>
<th>رقم الرسالة</th>
<th>الموضوع</th>
<th style="text-align :center">استلام</th>
</thead>
<tbody>
<tr t-foreach="data" t-as="rec" t-att-data-id="rec.id" t-att-data-res_model="rec.res_model" t-att-data-res_id="rec.res_id" t-att-data-base_url="rec.base_url" class="message_rec">
<td><span t-esc="rec.id"/></td>
<td><span t-esc="rec.subject" /></td>
<td style="text-align :center"><button class="o_icon_button" type="button" title="إستلام المعاملة"><i class="fa fa-check" title="إستلام المعاملة" /></button></td>
</tr>
</tbody>
</table>
<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
<div class="ui-dialog-buttonset">
<div class="btn btn-primary btn-sm" id="close" type="object" title="إستلام المعاملة">إغلاق</div>
</div>
</div> -->
</t>
</templates>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--add by fatima-ahmed 17/5/2020 to migration code of notify in -->
<template id="assets_backend_custom" name="autocomplete assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/exp_late_mail_reminder/static/src/css/style.css"/>
<script type="text/javascript" src="/exp_late_mail_reminder/static/src/js/notification_popup.js"/>
</xpath>
</template>
</odoo>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id='view_late_mail_reminder_form' model='ir.ui.view'>
<field name="name">late.mail.reminder.form</field>
<field name="model">late.email.reminder</field>
<field name="arch" type="xml">
<form string="Late Email Reminder" class="oe_form_configuration">
<header>
<button string="Apply" type="object" name="execute" class="oe_highlight"/>
<!-- <div>-->
<!-- or-->
<!-- </div>-->
<!-- <div/>-->
<!-- <button string="Cancel" type="object" name="cancel" class="oe_link"/>-->
</header>
<sheet>
<group>
<group>
<field name="number_of_days" required="1"/>
</group>
</group>
<notebook>
<page string="Email Late Line">
<group>
<field name="line_ids" nolabel="1">
<form string="Late Email Line">
<group>
<group>
<field name="model_id"/>
<field name="order_date"/>
</group>
<group>
<field name="record_state_ids" widget="many2many_tags"/>
<field name="group_ids" widget="many2many_tags"/>
<field name="template_id"/>
</group>
</group>
</form>
<tree string="Late Email Lines" editable="bottom">
<field name="model_id" required="1"/>
<field name="order_date" required="1"/>
<field name="record_state_ids" widget="many2many_tags" required="1"/>
<field name="group_ids" widget="many2many_tags" required="1"/>
<field name="template_id" required="1"/>
</tree>
</field>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id='action_late_email_reminder' model='ir.actions.act_window'>
<field name="name">late Email Reminder</field>
<field name="res_model">late.email.reminder</field>
<field name="view_mode">form</field>
<field name="res_id" ref="exp_late_mail_reminder.exp_late_email_reminder"/>
<field name="target">inline</field>
</record>
<menuitem action="action_late_email_reminder" name="Late Email Reminder" parent="base.menu_email"
id="menu_late_reminder_email" sequence="115"/>
</data>
</odoo>

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odex - Communications Management System.
# Copyright (C) 2019 Expert Co. Ltd. (<http://exp-sa.com>).
#
##############################################################################
{
'name': 'Multi-level Transaction Management',
'version': '1.0',
'sequence': 4,
'author': 'Expert Co. Ltd.',
'category': 'Odex25-Transactions/Odex25-Transactions',
'summary': 'Multi-level management of transactions',
'description': """
Odex - Communications Management System
========================================
Multi-level management of transactions
""",
'website': 'http://www.exp-sa.com',
'depends': ['exp_transaction_documents'],
'data': [
'views/extend_entity.xml',
],
'qweb' : [
],
'installable': True,
'auto_install': False,
'application': True,
}

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import extend_entity

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api,_
# from odoo.exceptions import Warning
class Entity(models.Model):
_inherit = 'cm.entity'
_description = 'for mange transaction in multi level'
manager_entity = fields.Many2many(comodel_name='cm.entity', relation='manage_entity_rel', column1='manager_id',
column2='entity_id', string='Owners of powers')
need_multi_approve = fields.Boolean(string='Need Multi level Approve')

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!--inherit entity view -->
<record id="entity_view_inherit_form" model="ir.ui.view">
<field name="name">extend.transaction.form</field>
<field name="model">cm.entity</field>
<field name="inherit_id" ref="exp_transaction_documents.cm_entity_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='need_approve']" position="after">
<field name="need_multi_approve" attrs="{'invisible': [('type', 'not in', ['unit'])]}"/>
<field name="manager_entity" domain="[('type', '=', 'employee')]" options="{'no_create_edit': True}"
widget="many2many_tags" attrs="{'invisible': [('need_multi_approve', '!=', True)]}"/>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/exp_transaction.iml" filepath="$PROJECT_DIR$/.idea/exp_transaction.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="3ff197a9-0b96-4ea8-9e34-af98cfa90616" name="Default Changelist" comment="" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/__manifest__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="300">
<caret line="20" column="16" selection-start-line="20" selection-start-column="16" selection-end-line="20" selection-end-column="16" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/models/configuration.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="26" selection-start-line="15" selection-start-column="26" selection-end-line="15" selection-end-column="26" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/__manifest__.py" />
<option value="$PROJECT_DIR$/models/configuration.py" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="133" />
<option name="y" value="47" />
<option name="width" value="1380" />
<option name="height" value="833" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane" />
<pane id="Scope" />
</panes>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="3ff197a9-0b96-4ea8-9e34-af98cfa90616" name="Default Changelist" comment="" />
<created>1555402175295</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1555402175295</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="67" y="25" width="1533" height="875" extended-state="6" />
<editor active="true" />
<layout>
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.24966888" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Version Control" order="7" />
<window_info anchor="bottom" id="Terminal" order="8" />
<window_info anchor="bottom" id="Event Log" order="9" side_tool="true" />
<window_info anchor="bottom" id="Python Console" order="10" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/__manifest__.py</url>
<line>20</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/__manifest__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="300">
<caret line="20" column="16" selection-start-line="20" selection-start-column="16" selection-end-line="20" selection-end-column="16" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/models/configuration.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="26" selection-start-line="15" selection-start-column="26" selection-end-line="15" selection-end-column="26" />
</state>
</provider>
</entry>
</component>
</project>

View File

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

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Odex - Communications Management System.
# Copyright (C) 2019 Expert Co. Ltd. (<http://exp-sa.com>).
#
##############################################################################
{
'name': 'Communications Management',
'version': '1.0',
'sequence': 4,
'author': 'Expert Co. Ltd.',
'category': 'Odex25-Transactions/Odex25-Transactions',
'summary': 'Correspondence Management System',
'description': """
Odex - Communications Management System
========================================
Managing Communications Transcations flows
""",
'website': 'http://www.exp-sa.com',
'depends': ['base', 'base_odex', 'mail', 'html_text', 'odex_sms'],
'data': [
'security/groups.xml',
'security/ir.model.access.csv',
'email_templates/out_templates.xml',
'data/cm_data.xml',
'data/ir_cron.xml',
'views/entity.xml',
'views/configuration.xml',
'views/transcation_common_view.xml',
'views/internal.xml',
'views/incoming.xml',
'views/outgoing.xml',
'reports/transaction_details_report_template.xml',
'reports/receiver_transaction_report_template.xml',
# 'views/settings_config_view.xml',
'views/actions_and_menus.xml',
'wizard/reject_transaction_reson.xml',
'wizard/forward_transaction.xml',
'wizard/archive_transaction.xml',
'wizard/transaction_reply_wizard.xml',
'wizard/reopen_transaction_wizard.xml',
'email_templates/internal_templates.xml',
'email_templates/incoming_templates.xml',
],
'qweb': [
],
'installable': True,
'auto_install': False,
'application': True,
}

View File

@ -0,0 +1,38 @@
<odoo>
<data>
<record id="seq_cm_entity" model="ir.sequence">
<field name="name">Entities Seq</field>
<field name="code">cm.entity</field>
<field name="prefix"></field>
<field name="suffix"></field>
<field name="padding">3</field>
<field name="company_id" eval="False"/>
</record>
<record id="seq_cm_transaction_out" model="ir.sequence">
<field name="name">Outgointg Transactions Seq</field>
<field name="code">cm.transaction.out</field>
<field name="prefix">%(year)s/2</field>
<field name="suffix"></field>
<field name="padding">4</field>
</record>
<record id="seq_cm_transaction_in" model="ir.sequence">
<field name="name">Incoming Transactions Seq</field>
<field name="code">cm.transaction.in</field>
<field name="prefix">%(year)s/3</field>
<field name="suffix"></field>
<field name="padding">4</field>
</record>
<record id="seq_cm_transaction_internal" model="ir.sequence">
<field name="name">Internal Transactions Seq</field>
<field name="code">cm.transaction.internal</field>
<field name="prefix">%(year)s/1</field>
<field name="suffix"></field>
<field name="padding">4</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,15 @@
<odoo>
<data>
<record model="ir.cron" id="late_transaction_mail">
<field name="name">تأخر المعاملات عن وقت الانتهاء</field>
<field name="active">True</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_internal_transaction" />
<field name="state">code</field>
<field name="code">model.late_transaction_cron()</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,65 @@
<odoo>
<data>
<record id="incoming_notify_send_send_email" model="mail.template">
<field name="name">Transaction Send Message</field>
<field name="model_id" ref="exp_transaction_documents.model_incoming_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.get_email()|safe}</field>
<field name="subject"><![CDATA[ معاملة خارجية واردة ]]></field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.get_name()}</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right;direction: ltr;float: left;">
توجد معاملة خارجية واردة للمعاملة
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و تنفيذ الإجراء المطلوب
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,719 @@
<odoo>
<data>
<record id="internal_approval1_request_email" model="mail.template">
<field name="name">Unit Manager Request Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${object.employee_id.name} <${(object.employee_id.user_id.partner_id.email)|safe}>]]></field>
<field name="email_to">${object.preparation_id.manager_id.user_id.partner_id.email|safe}</field>
<field name="subject"><![CDATA[ إعتماد الإدارة لمعاملة داخلية موجهة الى ]]> ${object.to_ids[0].name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right"> ،، ${object.preparation_id.manager_id.name|safe} - السادة الفضلاء</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
توجد معاملة داخلية في انتظار اعتماد الإدارة
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>موجهة الى : </b></td>
<td style="text-decoration:underline">${object.to_ids[0].name}</td>
</tr>
<tr>
<td><b> : موضوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b> : نوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> : الإدارة المعدة</b></td>
<td style="text-decoration:underline">${object.preparation_id.name}</td>
</tr>
<tr>
<td><b> : معِد المعاملة</b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> : آخر موعد لإنجاز المعاملة</b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<!-- <record id="internal_ready_email" model="mail.template">-->
<!-- <field name="name">Transaction Ready for send Message</field>-->
<!-- <field name="model_id" ref="cm_odex.model_cm_transaction_internal"/>-->
<!-- <field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>-->
<!-- <field name="email_to">${object.employee_id.user_id.partner_id.email|safe}</field>-->
<!-- <field name="subject"><![CDATA[ تم إعتماد الإدارة لمعاملة موجهة الى ]]> ${object.to_ids[0].name|safe} <![CDATA[ بالرقم ]]> ${object.name|safe}</field>-->
<!-- <field name="body_html">-->
<!-- <![CDATA[-->
<!-- <div style="text-align: right;direction:rtl">-->
<!-- <p style="text-align: right"> ،، ${object.employee_id.name} - السادة الفضلاء</p>-->
<!-- <p style="text-align: left">حفظهم الله،،</p>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- السلام عليكم ورحمة الله.-->
<!-- -->
<!-- </p>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- تم اعتماد الادارة للمعاملة-->
<!-- </p><br/>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- يمكنكم الان المتابعة و إرسال المعاملة المطلوبة -->
<!-- </p><br/>-->
<!-- <p style="font-size: 1em;text-align: right">-->
<!-- <table>-->
<!-- <tr>-->
<!-- <td><b>رقم المعاملة : </b></td>-->
<!-- <td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b>موجهة إالى : </b></td>-->
<!-- <td style="text-decoration:underline">${object.to_ids[0].name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : موضوع المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.subject}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : نوع المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.subject_type_id.name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : معِد المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.employee_id.name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : آخر موعد لإنجاز المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.due_date}</td>-->
<!-- </tr>-->
<!-- </table> -->
<!-- </p><br/>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- <a href="${object.get_url()}">رابط المعاملة</a>-->
<!-- </p>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- <a href="${user.company_id.website}">${user.company_id.name}</a>-->
<!-- -->
<!-- -->
<!-- </p>-->
<!-- </div>-->
<!-- ]]>-->
<!-- </field>-->
<!-- </record>-->
<!-- <record id="internal_ready_email2" model="mail.template">-->
<!-- <field name="name">Transaction Ready for send Message</field>-->
<!-- <field name="model_id" ref="cm_odex.model_cm_transaction_internal"/>-->
<!-- <field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>-->
<!-- <field name="email_to">${object.employee_id.user_id.partner_id.email|safe}</field>-->
<!-- <field name="subject"><![CDATA[ تم إعتماد القسم لمعاملة موجهة الى ]]> ${object.to_ids[0].name|safe} <![CDATA[ بالرقم ]]> ${object.name|safe}</field>-->
<!-- <field name="body_html">-->
<!-- <![CDATA[-->
<!-- <div style="text-align: right;direction:rtl">-->
<!-- <p style="text-align: right"> ،، ${object.employee_id.name} - السادة الفضلاء</p>-->
<!-- <p style="text-align: left">حفظهم الله،،</p>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- السلام عليكم ورحمة الله.-->
<!-- </p>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- تم اعتماد القسم للمعاملة رقم-->
<!-- </p><br/>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- يمكنكم الان المتابعة و إرسال المعاملة المطلوبة-->
<!-- </p><br/>-->
<!-- <p style="font-size: 1em;text-align: right">-->
<!-- <table>-->
<!-- <tr>-->
<!-- <td><b>رقم المعاملة : </b></td>-->
<!-- <td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b>موجهة إالى : </b></td>-->
<!-- <td style="text-decoration:underline">${object.to_ids[0].name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : موضوع المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.subject}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : نوع المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.subject_type_id.name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : معِد المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.employee_id.name}</td>-->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td><b> : آخر موعد لإنجاز المعاملة</b></td>-->
<!-- <td style="text-decoration:underline">${object.due_date}</td>-->
<!-- </tr>-->
<!-- </table>-->
<!-- </p><br/>-->
<!-- <p style="font-size: 1.1em;text-align: right;">-->
<!-- <a href="${object.get_url()}">رابط المعاملة</a>-->
<!-- </p>-->
<!-- <p style="font-size: 1.1em;text-align: right">-->
<!-- <a href="${user.company_id.website}">${user.company_id.name}</a>-->
<!-- </p>-->
<!-- </div>-->
<!-- ]]>-->
<!-- </field>-->
<!-- </record>-->
<record id="internal_notify_forward_email" model="mail.template">
<field name="name">Transaction forward Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.forward_user_id.partner_id.email|safe}</field>
<field name="subject"><![CDATA[ إحالة واردة: معاملة داخلية بالرقم ]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.forward_user_id.name}</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
توجد إحالة واردة للمعاملة
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و تنفيذ الإجراء المطلوب
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b>محالة من قِبل : </b></td>
<td style="text-decoration:underline">${object.get_latest_forward().from_id.name}</td>
</tr>
<tr>
<td><b>الإجراء المطلوب : </b></td>
<td style="text-decoration:underline">${object.get_latest_forward().procedure_id.name}</td>
</tr>
<tr>
<td><b>تاريخ الإحالة : </b></td>
<td style="text-decoration:underline">${object.get_latest_forward().date}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="internal_notify_reply_email" model="mail.template">
<field name="name">Transaction Reply Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.last_forwarded_user.partner_id.email|safe}</field>
<field name="subject"><![CDATA[ رد على إحالة: معاملة داخلية بالرقم ]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.employee_id.name}</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
تم الرد على الإحالة للمعاملة
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و إكمال اللازم
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b>موجه من : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('reply').from_id.name}</td>
</tr>
<tr>
<td><b>الإجراء المطلوب : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('reply').procedure_id.name}</td>
</tr>
<tr>
<td><b> الملاحظات : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('reply').note}</td>
</tr>
<tr>
<td><b>تاريخ الإحالة : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('reply').date}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="internal_notify_close_email" model="mail.template">
<field name="name">Transaction Close Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.employee_id.user_id.partner_id.email|safe}</field>
<field name="subject"><![CDATA[ حفظ المعاملة بالرقم ]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
تم حفظ المعاملة
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b>مبرر الحفظ : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('archive').archive_type_id.name}</td>
</tr>
<tr>
<td><b> الملاحظات : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('archive').note}</td>
</tr>
<tr>
<td><b>تاريخ الحفظ : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('archive').date}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="internal_notify_send_send_email" model="mail.template">
<field name="name">Transaction Send Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.get_email()|safe}</field>
<field name="subject"><![CDATA[ تم ارسال المعاملة : معاملة داخلية بالرقم ]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.get_name()}</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
توجد معاملة مرسلة
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و تنفيذ الإجراء المطلوب
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="internal_reopen_transaction_email" model="mail.template">
<field name="name">Reopen Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.employee_id.user_id.partner_id.email|safe}</field>
<field name="subject"><![CDATA[ إعادة فتح المعاملة]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.employee_id.name}</p>
<p style="text-align: center">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
تم اعادة فتح المعاملة
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b>الإجراء المطلوب : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('reopen').procedure_id.name}</td>
</tr>
<tr>
<td><b> الملاحظات : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('reopen').note}</td>
</tr>
<tr>
<td><b>تاريخ اعادة فتح المعاملة : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('reopen').date}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و تنفيذ الإجراء المطلوب
</p><br/>
<br/>
<p style="font-size: 1.1em;text-align: right">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="internal_return_transaction_email" model="mail.template">
<field name="name">Return Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.employee_id.user_id.partner_id.email|safe}</field>
<field name="subject"><![CDATA[ إرجاع المعاملة]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.employee_id.name}</p>
<p style="text-align: center">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
تم إرجاع المعاملة
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b> الملاحظات : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('return').note}</td>
</tr>
<tr>
<td><b>تاريخ إرجاع المعاملة : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('return').date}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و تنفيذ الإجراء المطلوب
</p><br/>
<br/>
<p style="font-size: 1.1em;text-align: right">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="internal_reject_transaction_email" model="mail.template">
<field name="name">Reject Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.employee_id.user_id.partner_id.email|safe}</field>
<field name="subject"><![CDATA[رفض المعاملة]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.employee_id.name}</p>
<p style="text-align: center">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
تم رفض المعاملة
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b> اسباب الرفض : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('refuse').note}</td>
</tr>
<tr>
<td><b>تاريخ رفض المعاملة : </b></td>
<td style="text-decoration:underline">${object.get_latest_by_action('refuse').date}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و تنفيذ الإجراء المطلوب
</p><br/>
<br/>
<p style="font-size: 1.1em;text-align: right">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="internal_late_transaction_email" model="mail.template">
<field name="name">Late Message</field>
<field name="model_id" ref="exp_transaction_documents.model_internal_transaction"/>
<field name="email_from"><![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">${object.get_email()}</field>
<field name="subject"><![CDATA[تأخر المعاملة]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right">السادة الفضلاء: ${object.employee_id.name}</p>
<p style="text-align: center">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
تأخر معاملة
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>موضوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b>نوع المعاملة : </b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> معِد المعاملة : </b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> آخر موعد لانجاز المعاملة : </b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و تنفيذ الإجراء المطلوب
</p><br/>
<br/>
<p style="font-size: 1.1em;text-align: right">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="out_approval1_request_email" model="mail.template">
<field name="name">Unit Manager Approval Request Message</field>
<field name="model_id" ref="exp_transaction_documents.model_outgoing_transaction"/>
<field name="email_from">
<![CDATA[${object.employee_id.name} <${(object.employee_id.user_id.partner_id.email)|safe}>]]></field>
<field name="email_to">${object.preparation_id.manager_id.user_id.partner_id.email|safe}</field>
<field name="subject"><![CDATA[ إعتماد معاملة صادر موجهة الى ]]> ${object.to_ids[0].name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right"> ،، ${object.preparation_id.manager_id.name} - السادة الفضلاء</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
توجد معاملة صادر جديدة في انتظار اعتماد مسئول الوحدة (${object.preparation_id.name})
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>جهة الصادر : </b></td>
<td style="text-decoration:underline">${object.to_ids[0].name}</td>
</tr>
<tr>
<td><b> : موضوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b> : نوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> : معِد المعاملة</b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> : آخر موعد لإنجاز المعاملة</b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="out_approval2_request_email" model="mail.template">
<field name="name">CEO Approval Request Message</field>
<field name="model_id" ref="exp_transaction_documents.model_outgoing_transaction"/>
<field name="email_from">
<![CDATA[${object.employee_id.name} <${(object.employee_id.user_id.partner_id.email)|safe}>]]></field>
<field name="email_to">${object.get_second_manager().partner_id.email|safe}</field>
<field name="subject"><![CDATA[ إعتماد الإدارة التنفيذية لمعاملة صادر موجهة الى ]]>
${object.to_ids[0].name|safe}
</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right"> ،، ${object.get_second_manager().name|safe} - السادة الفضلاء</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
توجد معاملة صادر جديدة في انتظار اعتماد الإدارة التنفيذية
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>جهة الصادر : </b></td>
<td style="text-decoration:underline">${object.to_ids[0].name}</td>
</tr>
<tr>
<td><b> : موضوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b> : نوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> : معِد المعاملة</b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> : الإدارة المعدّة</b></td>
<td style="text-decoration:underline">${object.preparation_id.name}</td>
</tr>
<tr>
<td><b> : آخر موعد لإنجاز المعاملة</b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="out_ready_email" model="mail.template">
<field name="name">Transaction Ready for send Message</field>
<field name="model_id" ref="exp_transaction_documents.model_outgoing_transaction"/>
<field name="email_from">
<![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">
${object.preparation_id.manager_id.user_id.partner_id.email|safe},${object.employee_id.user_id.partner_id.email|safe}
</field>
<field name="subject"><![CDATA[ تم إعتماد الإدارة التنفيذية لمعاملة صادر موجهة الى ]]>
${object.to_ids[0].name|safe} <![CDATA[ بالرقم ]]> ${object.name|safe}
</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right"> ،، ${object.employee_id.name} - السادة الفضلاء</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1.1em;text-align: right">
تم اعتماد الادارة التنفيذية لمعاملة الصادر
</p><br/>
<p style="font-size: 1.1em;text-align: right">
يمكنكم الان المتابعة و ترميز المعاملة كمرسل حال اكتمال اجراءات الارسال،،
</p><br/>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>جهة الصادر : </b></td>
<td style="text-decoration:underline">${object.to_ids[0].name}</td>
</tr>
<tr>
<td><b> : موضوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b> : نوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> : معِد المعاملة</b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
<tr>
<td><b> : آخر موعد لإنجاز المعاملة</b></td>
<td style="text-decoration:underline">${object.due_date}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
<record id="out_email" model="mail.template">
<field name="name">Transaction Ready for send Message</field>
<field name="model_id" ref="exp_transaction_documents.model_outgoing_transaction"/>
<field name="email_from">
<![CDATA[${user.company_id.name} <${(user.company_id.email or user.email)|safe}>]]></field>
<field name="email_to">
${object.preparation_id.manager_id.user_id.partner_id.email|safe},${object.employee_id.user_id.partner_id.email|safe}
</field>
<field name="subject">${object.to_ids[0].name|safe} <![CDATA[ بالرقم ]]> ${object.name|safe}</field>
<field name="body_html">
<![CDATA[
<div style="text-align: right;direction:rtl">
<p style="text-align: right"> ،، ${object.employee_id.name} - السادة الفضلاء</p>
<p style="text-align: left">حفظهم الله،،</p>
<p style="font-size: 1.1em;text-align: right">
السلام عليكم ورحمة الله.
</p>
<p style="font-size: 1em;text-align: right">
<table>
<tr>
<td><b>رقم المعاملة : </b></td>
<td style="text-decoration:underline;direction: ltr;float: left;">${object.name}</td>
</tr>
<tr>
<td><b>جهة الصادر : </b></td>
<td style="text-decoration:underline">${object.to_ids[0].name}</td>
</tr>
<tr>
<td><b> : موضوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject}</td>
</tr>
<tr>
<td><b> : نوع المعاملة</b></td>
<td style="text-decoration:underline">${object.subject_type_id.name}</td>
</tr>
<tr>
<td><b> : معِد المعاملة</b></td>
<td style="text-decoration:underline">${object.employee_id.name}</td>
</tr>
</table>
</p><br/>
<p style="font-size: 1.1em;text-align: right;">
<a href="${object.get_url()}">رابط المعاملة</a>
</p>
<p style="font-size: 1.1em;text-align: right">
<a href="${user.company_id.website}">${user.company_id.name}</a>
</p>
</div>
]]>
</field>
</record>
</data>
</odoo>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
from . import entity
from . import configuration
# from . import res_config_settings
from . import transaction
from . import internal_transaction
from . import outgoing_transaction
from . import incoming_transaction
from . import tools

View File

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
from odoo import models, api, fields, _
TRACE_ACTIONS = [
('forward', _('Forwarded')),
('receive', _('Received')),
('archive', _('Archived')),
('approve', _('Unit Manager Approved')),
('ceo_approve', _('CEO Approved')),
('sent', _('Sent')),
('return', _('Returned')),
('action', _('Action Taken')),
('refuse', _('Refused')),
('reply', _('Replied')),
('waite', _('Waiting Approve')),
('reopen', _('Reopened')),
]
class SubjectType(models.Model):
_name = 'cm.subject.type'
_order = 'sequence'
name = fields.Char(string='Transaction Type')
sequence = fields.Integer(string='Sequence', default=5)
second_approval = fields.Boolean(string='Second Approval ?',
help='Check if this transaction type need a second (CEO) Approval.', default=True)
transaction_need_approve = fields.Boolean(string="Transaction need approve")
tran_tag = fields.Many2many(comodel_name='transaction.tag', string='Tags')
class ImportantDegree(models.Model):
_name = 'cm.transaction.important'
name = fields.Char(string='Important Degree')
rank = fields.Integer(string='Transaction Rank')
class Procedure(models.Model):
_name = 'cm.procedure'
name = fields.Char(string='Procedure Name')
class AttachmentType(models.Model):
_name = 'cm.attachment.type'
sequence = fields.Integer(string='Sequence', default=1)
name = fields.Char(string='Name')
class Attachment(models.Model):
_name = 'cm.attachment'
name = fields.Char(string='Description')
num_page = fields.Integer(string='No. Pages')
type_id = fields.Many2one('cm.attachment.type', string='Attachment type')
incoming_transaction_id = fields.Many2one(comodel_name='incoming.transaction', string='Incoming Transaction')
internal_transaction_id = fields.Many2one(comodel_name='internal.transaction', string='Internal Transaction')
outgoing_transaction_id = fields.Many2one(comodel_name='outgoing.transaction', string='Outgoing Transaction')
class ArchiveType(models.Model):
_name = 'cm.archive.type'
name = fields.Char(string='Archive Type')
class AttachmentRule(models.Model):
_name = 'cm.attachment.rule'
def _default_employee_id(self):
user = self.env.user
em = self.env['cm.entity'].search([('user_id', '=', user.id)], limit=1)
return len(em) and em or self.env['cm.entity']
name = fields.Char()
employee_id = fields.Many2one(comodel_name='cm.entity', string='Created By',
default=lambda self: self._default_employee_id(), readonly="True")
entity_id = fields.Many2one(comodel_name='cm.entity', string='Unit Responsible', related='employee_id.parent_id',
store=True)
file_save = fields.Binary('Save File')
attachment_filename = fields.Char(string="Attachment Filename")
incoming_transaction_id = fields.Many2one(comodel_name='incoming.transaction', string='Incoming Transaction')
internal_transaction_id = fields.Many2one(comodel_name='internal.transaction', string='Internal Transaction')
outgoing_transaction_id = fields.Many2one(comodel_name='outgoing.transaction', string='Outgoing Transaction')
date = fields.Datetime(string='Date', default=fields.Datetime.now)
description = fields.Char(string='Description')
class TransactionTrace(models.Model):
_name = 'cm.transaction.trace'
_description = 'Transaction Trace'
_order = 'date desc'
action = fields.Selection(string='Action', selection=TRACE_ACTIONS, default='forward')
incoming_transaction_id = fields.Many2one(comodel_name='incoming.transaction', string='Incoming Transaction')
internal_transaction_id = fields.Many2one(comodel_name='internal.transaction', string='Internal Transaction')
outgoing_transaction_id = fields.Many2one(comodel_name='outgoing.transaction', string='Outgoing Transaction')
date = fields.Datetime(string='Date', default=fields.Datetime.now)
from_id = fields.Many2one(comodel_name='cm.entity', string='From')
to_id = fields.Many2one(comodel_name='cm.entity', string='To')
procedure_id = fields.Many2one(comodel_name='cm.procedure', string='Action Taken')
note = fields.Char(string='Notes')
archive_type_id = fields.Many2one(comodel_name='cm.archive.type', string='Archive Type')
cc_ids = fields.Many2many('cm.entity', string='CC To')
class ProjectType(models.Model):
_name = "project.type"
name = fields.Char(string='Name')
sequence = fields.Integer(string='Sequence', default=1)
class TransactionCategory(models.Model):
_name = 'transaction.tag'
name = fields.Char("Name")

View File

@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
import datetime
from odoo import models, api, fields, _
from odoo.exceptions import ValidationError, UserError
class JobTitle(models.Model):
_name = 'cm.job.title'
_description = 'Job Titles'
name = fields.Char(string='Job Title')
class Entity(models.Model):
_name = 'cm.entity'
_description = 'Transactions Contacts'
_order = 'name'
@api.model
def _name_search(self, name, args=None, operator='like', limit=100, name_get_uid=None):
args = args or []
domain = []
if name:
domain = ['|', ('name', operator, name), ('code', operator, name)]
print(domain)
return self._search(domain + args, limit=limit, access_rights_uid=name_get_uid)
@api.constrains('code')
def _check_code(self):
count = self.search_count([('code', '=', self.code), ('id', '!=', self.id)])
if self.code:
if count:
raise ValidationError(_("Validation Error Entity Code Must Be unique !"))
if self.type == 'unit':
x = ''
if len(self.code) == 3 or len(self.code) == 2:
x = 'a'
if self.code.isalpha() == False or x == '':
raise ValidationError(_("Validation Error Entity Code Must Be Composed from 3/2 characters"))
code = fields.Char(string='Code')
# sequence = fields.Integer(string='Sequence')
partner_id = fields.Many2one(comodel_name='res.partner', string='Partner', readonly=False, ondelete='cascade',
copy=False)
name = fields.Char(string='Name', store=True)
type = fields.Selection(string='Entity Type', selection=[('unit', _('Internal Unit')), ('employee', _('Employee')),
('external', _('External Unit'))], default='unit')
parent_id = fields.Many2one(comodel_name='cm.entity', string='Parent Entity')
department_id = fields.Many2one('hr.department')
manager_id = fields.Many2one(comodel_name='cm.entity', string='Unit Manager')
secretary_id = fields.Many2one(comodel_name='cm.entity', string='Employee in charge of transactions')
user_id = fields.Many2one(comodel_name='res.users', string='Related User', related='employee_id.user_id')
# job_title_id = fields.Many2one(comodel_name='cm.job.title', string='Job Title')
job_title_id = fields.Many2one(comodel_name='hr.job', string='Job Title')
need_approve = fields.Boolean(string='Need Aprove')
executive_direction = fields.Boolean(string='Executive direction')
is_secret = fields.Boolean(string='Is Secret')
person_id = fields.Char(string='Person ID')
person_id_issue_date = fields.Date(string='Person ID Issue Date')
employee_assignment_date = fields.Date(string='Employee Assignment Date')
employee_id = fields.Many2one('hr.employee')
phone = fields.Char()
email = fields.Char()
child_ids = fields.Many2many(comodel_name='cm.entity', relation='employee_entity_rel', column1='employee_id',
column2='entity_id', string='Related Units')
establish_date = fields.Date(string='Establish Date')
unit_location = fields.Char(string='Unit Location')
sketch_attachment_id = fields.Many2one(comodel_name='ir.attachment', string='Sketch Attachment')
dynamic_year = fields.Char(string='Year', default=datetime.datetime.now().strftime('%Y'))
year_increment = fields.Boolean(string='Continue Increment every year?', help='''
Check if you want to continue incrementing in the start of every new year.
''', default=True)
@api.onchange('department_id')
def onchange_department_id(self):
self.name = self.department_id.name
@api.onchange('employee_id')
def onchange_employee_id(self):
self.job_title_id = self.employee_id.job_id
self.name = self.employee_id.name
self.person_id = self.employee_id.iqama_number.iqama_id
self.email = self.employee_id.personal_email
self.phone = self.employee_id.mobile_phone
self.person_id_issue_date = self.employee_id.iqama_number.expiry_date
# self.employee_assignment_date = self.employee_id.job_id
@api.onchange('partner_id')
def onchange_partner_id(self):
self.name = self.partner_id.name
self.email = self.partner_id.email
self.phone = self.partner_id.phone
####################################################
# ORM Overrides methods
####################################################
@api.model
def create(self, vals):
if vals.get('type', False) == 'employee':
vals['partner_id'] = self.env['hr.employee'].search(
[('id', '=', vals['employee_id'])]).user_id.partner_id.id
if 'partner_id' not in vals:
print("*******************")
if vals.get('type', False) == 'employee':
user_id = vals.get('user_id', False)
if user_id:
vals['partner_id'] = self.env['res.users'].search([('id', '=', user_id)]).partner_id.id
else:
partner = self.env['res.partner'].create({
'name': vals.get('name', ''),
'email': vals.get('email', ''),
'city': vals.get('city', _('Riyadh')),
'is_company': vals.get('is_company', True),
'country_id': self.env.ref('base.sa', True).id,
})
vals['partner_id'] = partner.id
sequence = {
'employee': '01',
'unit': '02',
'external': '03',
}
if not vals.get('code', False):
seq = self.env['ir.sequence'].get('cm.entity')
s = u'{}-{}'.format(sequence[vals.get('type', 'employee')], seq)
if vals.get('type') == 'employee' or vals.get('type') == 'external':
vals['code'] = s
return super(Entity, self).create(vals)
def write(self, vals):
sequence = {
'employee': '01',
'unit': '02',
'external': '03',
}
if not vals.get('code', False):
seq = self.env['ir.sequence'].get('cm.entity')
s = u'{}-{}'.format(sequence[vals.get('type', 'employee')], seq)
if vals.get('type') == 'employee' or vals.get('type') == 'external':
vals['code'] = s
return super(Entity, self).write(vals)
def copy(self, default=None):
raise UserError(_('You cannot duplicate an entity!'))
class ResPartner(models.Model):
_inherit = 'res.partner'
is_transaction_entity = fields.Boolean('Is Transaction Entity?')
@api.model
def create(self, values):
res = super(ResPartner, self).create(values)
if values.get('is_transaction_entity'):
entity = self.env['cm.entity'].create({
'name': values.get('name', ''),
'partner_id': values.get('id'),
})
return res
def write(self, vals):
res = super(ResPartner, self).write(vals)
if vals.get('is_transaction_entity'):
if not self.env['cm.entity'].search([('partner_id', '=', self.id)]):
entity = self.env['cm.entity'].create({
'name': self.name,
'partner_id': vals.get('id'),
})
return res

View File

@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
from odoo import models, api, fields, _
from odoo.exceptions import ValidationError
from datetime import datetime
from hijri_converter import convert, Hijri
class IncomingTransaction(models.Model):
_name = 'incoming.transaction'
_inherit = ['transaction.transaction', 'mail.thread']
_description = 'incoming Transaction'
# due_date = fields.Date(string='Deadline', compute='compute_due_date')
from_id = fields.Many2one(comodel_name='cm.entity', string='Incoming From (External)')
partner_id = fields.Many2one('res.partner')
outgoing_transaction_id = fields.Many2one('outgoing.transaction', string='Related Outgoing')
incoming_number = fields.Char(string='Incoming Number')
incoming_date = fields.Date(string='Incoming Date', default=fields.Date.today)
incoming_date_hijri = fields.Char(string='Incoming Date (Hijri)', compute='_compute_incoming_date_hijri')
attachment_rule_ids = fields.One2many('cm.attachment.rule', 'incoming_transaction_id', string='Attaches')
attachment_ids = fields.One2many('cm.attachment', 'incoming_transaction_id', string='Attachments')
trace_ids = fields.One2many('cm.transaction.trace', 'incoming_transaction_id', string='Trace Log')
to_ids = fields.Many2many(comodel_name='cm.entity', relation='incoming_entity_rel', column1='incoming_id'
, column2='entity_id', string='Send To')
cc_ids = fields.Many2many(comodel_name='cm.entity', relation='incoming_entity_cc_rel',
column1='incoming_id', column2='entity_id', string='CC To')
tran_tag = fields.Many2many(comodel_name='transaction.tag', string='Tags')
tran_tag_unit = fields.Many2many(comodel_name='transaction.tag', string='Business unit',
relation='incoming_tag_rel',
column1='incoming_id'
, column2='name')
project_id = fields.Many2many('project.project')
sale_order_id = fields.Many2one('sale.order', 'Proposal')
processing_ids = fields.Many2many(comodel_name='incoming.transaction', relation='transaction_incoming_incoming_rel',
column1='transaction_id', column2='incoming_id',
string='Process Transactions incoming')
processing2_ids = fields.Many2many(comodel_name='outgoing.transaction',
relation='transaction_incoming_outgoing_rel',
column1='transaction_id', column2='outgoing_id',
string='Process Transactions Outgoing')
attachment_count = fields.Integer(compute='count_attachments')
# attachment_file = fields.Many2many(
# comodel_name='ir.attachment',
# string='')
datas = fields.Binary(string="", related='send_attach.datas')
def count_attachments(self):
obj_attachment = self.env['ir.attachment']
for record in self:
record.attachment_count = 0
attachment_ids = obj_attachment.search(
[('res_model', '=', 'incoming.transaction'), ('res_id', '=', record.id)])
first_file = []
if attachment_ids:
first_file.append(attachment_ids[0].id)
# print(first_file)
# record.attachment_file = first_file
record.attachment_count = len(attachment_ids)
@api.model
def get_url(self):
url = u''
action = self.env.ref(
'exp_transaction_documents.forward_incoming_external_tran_action', False)
Param = self.env['ir.config_parameter'].sudo()
if action:
return u'{}/web#id={}&action={}&model=incoming.transaction'.format(
Param.get_param('web.base.url', self.env.user.company_id.website), self.id, action.id)
return url
@api.depends('incoming_date')
def _compute_incoming_date_hijri(self):
for rec in self:
if rec.incoming_date:
gregorian_date = fields.Date.from_string(rec.incoming_date)
hijri_date = convert.Gregorian(gregorian_date.year, gregorian_date.month, gregorian_date.day).to_hijri()
rec.incoming_date_hijri = hijri_date
else:
rec.incoming_date_hijri = ''
@api.depends('attachment_rule_ids')
def compute_attachment_num(self):
for r in self:
r.attachment_num = len(r.attachment_rule_ids)
def fetch_sequence(self):
'''generate transaction sequence'''
return self.env['ir.sequence'].next_by_code('cm.transaction.in') or _('New')
####################################################
# Business methods
####################################################
def action_draft(self):
for record in self:
res = super(IncomingTransaction, self).action_send()
# Check if to_ids is not empty before accessing its first element
if record.to_ids:
employee = self.current_employee()
to_id = record.to_ids[0].id
if record.to_ids[0].type != 'employee':
to_id = record.to_ids[0].secretary_id.id
record.trace_ids.create({
'action': 'sent',
'to_id': to_id,
'from_id': employee and employee.id or False,
'procedure_id': record.procedure_id.id or False,
'incoming_transaction_id': record.id
})
partner_ids = []
for partner in record.to_ids:
if partner.type == 'unit':
partner_ids.append(partner.secretary_id.user_id.partner_id.id)
record.forward_user_id = partner.secretary_id.user_id.id
elif partner.type == 'employee':
partner_ids.append(partner.user_id.partner_id.id)
record.forward_user_id = partner.user_id.id
subj = _('Message Has been send !')
msg = _(u'{} &larr; {}').format(record.employee_id.name, u' / '.join([k.name for k in record.to_ids]))
msg = u'{}<br /><b>{}</b> {}.<br />{}'.format(msg,
_(u'Action Taken'), record.procedure_id.name,
u'<a href="%s" >رابط المعاملة</a> ' % (
record.get_url()))
self.action_send_notification(subj, msg, partner_ids)
template = 'exp_transaction_documents.incoming_notify_send_send_email'
self.send_message(template=template)
return res
def action_send_forward(self):
template = 'exp_transaction_documents.incoming_notify_send_send_email'
self.send_message(template=template)
def action_reply_internal(self):
name = 'default_incoming_transaction_id'
return self.action_reply_tran(name, self)
def action_forward_incoming(self):
name = 'default_incoming_transaction_id'
return self.action_forward_tran(name, self)
def action_archive_incoming(self):
name = 'default_incoming_transaction_id'
return self.action_archive_tran(name, self)
####################################################
# ORM Overrides methods
####################################################
@api.model
def create(self, vals):
seq = self.fetch_sequence()
if vals['preparation_id']:
code = self.env['cm.entity'].browse(vals['preparation_id']).code
x = seq.split('/')
sequence = "%s/%s/%s" % (x[0], code, x[1])
vals['name'] = sequence
else:
vals['name'] = seq
vals['ean13'] = self.env['odex.barcode'].code128('IN', vals['name'], 'TR')
return super(IncomingTransaction, self).create(vals)
#
#
# def unlink(self):
# if self.env.uid != 1:
# raise ValidationError(_("You can not delete transaction....."))
# return super(IncomingTransaction, self).unlink()

View File

@ -0,0 +1,212 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from odoo import models, api, fields, _
class InternalTransaction(models.Model):
_name = 'internal.transaction'
_inherit = ['transaction.transaction', 'mail.thread']
_description = 'internal Transaction'
# due_date = fields.Date(string='Deadline', compute='_compute_due_date')
reason = fields.Text('Reason')
attachment_rule_ids = fields.One2many('cm.attachment.rule', 'internal_transaction_id', string='Attaches')
attachment_ids = fields.One2many('cm.attachment', 'internal_transaction_id', string='Attachments')
trace_ids = fields.One2many('cm.transaction.trace', 'internal_transaction_id', string='Trace Log')
type_sender = fields.Selection(
string='',
selection=[('unit', 'Unit'),
('employee', 'Employee'),
],
required=False, default='unit')
to_ids = fields.Many2many(comodel_name='cm.entity', relation='internal_entity_rel', column1='internal_id'
, column2='entity_id', string='Send To')
partner_id = fields.Many2one('res.partner', string='Partner', readonly=True,
related='to_ids.secretary_id.partner_id')
cc_ids = fields.Many2many(comodel_name='cm.entity', relation='internal_entity_cc_rel',
column1='internal_id', column2='entity_id', string='CC To')
project_domain = fields.Many2many('project.project', string='Project Domain')
processing_ids = fields.Many2many(comodel_name='internal.transaction', relation='transaction_internal_rel',
column1='transaction_id', column2='internal_id', string='Process Transactions')
@api.model
def get_url(self):
url = u''
action = self.env.ref(
'exp_transaction_documents.incoming_internal_tran_action', False)
Param = self.env['ir.config_parameter'].sudo()
if action:
return u'{}/web#id={}&action={}&model=internal.transaction'.format(
Param.get_param('web.base.url', self.env.user.company_id.website), self.id, action.id)
return url
@api.depends('attachment_rule_ids')
def compute_attachment_num(self):
for r in self:
r.attachment_num = len(r.attachment_rule_ids)
def fetch_sequence(self, data=None):
'''generate transaction sequence'''
return self.env['ir.sequence'].get('cm.transaction.internal') or _('New')
####################################################
# Business methods
####################################################
def action_draft(self):
for record in self:
"""her i need to review code for to_ids"""
res = super(InternalTransaction, self).action_draft()
sent = 'sent'
template = 'exp_transaction_documents.internal_notify_send_send_email'
if record.subject_type_id.transaction_need_approve or record.preparation_id.need_approve:
template = 'exp_transaction_documents.internal_approval1_request_email'
sent = 'waite'
record.trace_create_ids('internal_transaction_id', record, sent)
partner_ids = []
for partner in record.to_ids:
if partner.type == 'unit':
partner_ids.append(partner.secretary_id.user_id.partner_id.id)
record.forward_user_id = partner.secretary_id.user_id.id
elif partner.type == 'employee':
partner_ids.append(partner.user_id.partner_id.id)
record.forward_user_id = partner.user_id.id
if record.to_user_have_leave:
record.forward_user_id = record.receive_id.user_id.id
record.send_message(template=template)
subj = _('Message Has been send !')
msg = _(u'{} &larr; {}').format(record.employee_id.name, u' / '.join([k.name for k in record.to_ids]))
msg = u'{}<br /><b>{}</b> {}.<br />{}'.format(msg,
_(u'Action Taken'), record.procedure_id.name,
u'<a href="%s" >رابط المعاملة</a> ' % (
record.get_url()))
company_id = self.env.user.company_id
if company_id.sms_active == True:
message = "There is a transaction that needs to " + self.procedure_id.name if self.procedure_id else ""
message += " with the number " + self.name
print(record.employee_id.employee_id.phone)
print(message)
request = company_id.send_sms(str(record.employee_id.employee_id.phone), message if message else "")
for rec in record:
rec.action_send_notification(subj, msg, partner_ids)
return res
def action_approve(self):
res = super(InternalTransaction, self).action_approve()
template = 'exp_transaction_documents.internal_notify_send_send_email'
self.send_message(template=template)
employee = self.current_employee()
to_id = self.to_ids[0].id
if self.to_ids[0].type != 'employee':
to_id = self.to_ids[0].secretary_id.id
self.trace_ids.create({
'action': 'sent',
'to_id': to_id,
'from_id': employee and employee.id or False,
'procedure_id': self.procedure_id.id or False,
'internal_transaction_id': self.id
})
# self.trace_create_ids('internal_transaction_id', self, 'sent')
subj = _('Message Has been approved !')
msg = _(u'{} &larr; {}').format(self.preparation_id.manager_id.name, u' / '.join([k.name for k in self.to_ids]))
msg = u'{}<br /><b>{}</b> {}.<br />{}'.format(msg,
_(u'Action Taken'), self.procedure_id.name,
u'<a href="%s" >رابط المعاملة</a> ' % (
self.get_url()))
partner_ids = [self.employee_id.user_id.partner_id.id, self.to_ids[0].user_id.partner_id.id]
self.action_send_notification(subj, msg, partner_ids)
return res
def action_reject_internal(self):
name = 'default_internal_transaction_id'
return self.action_reject(name, self)
def action_return_internal(self):
name = 'default_internal_transaction_id'
return self.action_return_tran(name, self)
def action_forward_internal(self):
name = 'default_internal_transaction_id'
return self.action_forward_tran(name, self)
def action_reply_internal(self):
name = 'default_internal_transaction_id'
return self.action_reply_tran(name, self)
def action_archive_internal(self):
name = 'default_internal_transaction_id'
return self.action_archive_tran(name, self)
def action_reopen_internal(self):
name = 'default_internal_transaction_id'
return self.action_reopen_tran(name, self)
def get_latest_forward(self):
for rec in self:
return rec.trace_ids.filtered(lambda z: z.action == 'forward')[0]
def get_latest_by_action(self, action):
for rec in self:
return rec.trace_ids.filtered(lambda z: z.action == action)[0]
def action_send_forward(self):
template = 'exp_transaction_documents.internal_notify_forward_email'
self.send_message(template=template)
def action_send_reply(self):
template = 'exp_transaction_documents.internal_notify_reply_email'
self.send_message(template=template)
def action_send_close(self):
template = 'exp_transaction_documents.internal_notify_close_email'
self.send_message(template=template)
def action_reopen_email(self):
template = 'exp_transaction_documents.internal_reopen_transaction_email'
self.send_message(template=template)
def action_reject_email(self):
template = 'exp_transaction_documents.internal_reject_transaction_email'
self.send_message(template=template)
def action_return_email(self):
template = 'exp_transaction_documents.internal_return_transaction_email'
self.send_message(template=template)
def late_transaction_cron(self):
templates = 'exp_transaction_documents.internal_late_transaction_email'
transaction_ids = self.env['internal.transaction'].search([('state', 'in', ['send', 'reply'])])
if transaction_ids:
today = fields.date.today()
for transaction in transaction_ids:
if datetime.strptime(transaction.due_date, "%Y-%m-%d") < datetime.strptime(str(today), "%Y-%m-%d"):
rec = transaction.trace_ids.filtered(lambda z: z.action == 'forward' or z.action == 'sent' or
z.action == 'reply')[0]
template = self.env.ref(templates, False)
template.write({'email_to': rec.to_id.user_id.partner_id.email,
'email_cc': rec.to_id.parent_id.manager_id.user_id.partner_id.email})
template.with_context(lang=self.env.user.lang).send_mail(
transaction.id, force_send=True, raise_exception=False)
####################################################
# ORM Overrides methods
####################################################
@api.model
def create(self, vals):
seq = self.fetch_sequence()
if vals.get('preparation_id', False):
code = self.env['cm.entity'].browse(vals['preparation_id']).code
x = seq.split('/')
sequence = "%s/%s/%s" % (x[0], code, x[1])
vals['name'] = sequence
else:
vals['name'] = seq
return super(InternalTransaction, self).create(vals)
#
# def unlink(self):
# if self.env.uid != 1:
# raise ValidationError(_("You can not delete transaction....."))
# return super(InternalTransaction, self).unlink()

View File

@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
from odoo import models, api, fields, _
from odoo.exceptions import ValidationError
class OutgoingTransaction(models.Model):
_name = 'outgoing.transaction'
_inherit = ['transaction.transaction', 'mail.thread']
_description = 'outgoing Transaction'
reason = fields.Text('Reason')
attachment_rule_ids = fields.One2many('cm.attachment.rule', 'outgoing_transaction_id', string='Attaches')
attachment_ids = fields.One2many('cm.attachment', 'outgoing_transaction_id', string='Attachments')
trace_ids = fields.One2many('cm.transaction.trace', 'outgoing_transaction_id', string='Trace Log')
is_partner = fields.Boolean()
partner_id = fields.Many2one('res.partner')
incoming_transaction_id = fields.Many2one('incoming.transaction', string='Related Incoming')
to_ids = fields.Many2many(comodel_name='cm.entity', relation='outgoing_entity_rel', column1='outgoing_id'
, column2='entity_id', string='Send To')
tran_tag = fields.Many2many(comodel_name='transaction.tag', string='Tags')
tran_tag_unit = fields.Many2many(comodel_name='transaction.tag', string='Business unit',
relation='outgoing_tag_rel',
column1='incoming_id'
, column2='name')
project_id = fields.Many2many('project.project')
sale_order_id = fields.Many2one('sale.order', 'Proposal')
to_name = fields.Char(string="Recipient")
cc_ids = fields.Many2many(comodel_name='cm.entity', relation='outgoing_entity_cc_rel',
column1='outgoing_id', column2='entity_id', string='CC To')
processing_ids = fields.Many2many(comodel_name='outgoing.transaction', relation='transaction_outgoing_outgoing_rel',
column1='transaction_id', column2='outgoing_id',
string='Process Transactions outgoing')
processing2_ids = fields.Many2many(comodel_name='incoming.transaction',
relation='transaction_outgoing_incoming_rel',
column1='transaction_id', column2='incoming_id',
string='Process Transactions incoming')
# processing_ids = fields.Many2many(comodel_name='transaction.transaction', relation='transaction_outgoing_rel',
# column1='transaction_id', column2='outgoing_id', string='Process Transactions')
@api.depends('attachment_rule_ids')
def compute_attachment_num(self):
for r in self:
r.attachment_num = len(r.attachment_rule_ids)
@api.model
def get_url(self):
url = u''
action = self.env.ref(
'exp_transaction_documents.outgoing_external_tran_action', False)
Param = self.env['ir.config_parameter'].sudo()
if action:
return u'{}/web#id={}&action={}&model=outgoing.transaction'.format(
Param.get_param('web.base.url', self.env.user.company_id.website), self.id, action.id)
return url
def fetch_sequence(self, data=None):
"""generate transaction sequence"""
return self.env['ir.sequence'].next_by_code('cm.transaction.out') or _('New')
####################################################
# Business methods
####################################################
#
def action_draft(self):
for record in self:
"""her i need to review code for to_ids"""
# res = super(OutgoingTransaction, self).action_draft()
if record.subject_type_id.transaction_need_approve or record.preparation_id.need_approve:
record.state = 'to_approve'
else:
record.state = 'complete'
# record.trace_create_ids('outgoing_transaction_id', record, 'sent')
partner_ids = [record.preparation_id.manager_id.user_id.partner_id.id]
subj = _('Message Has been send !')
msg = _(u'{} &larr; {}').format(record.employee_id.name, u' / '.join([k.name for k in record.to_ids]))
msg = u'{}<br /><b>{}</b> {}.<br />{}'.format(msg,
_(u'Action Taken'), record.procedure_id.name,
u'<a href="%s" >رابط المعاملة</a> ' % (
record.get_url()))
self.action_send_notification(subj, msg, partner_ids)
# return res
def action_email(self):
# todo#add email function here
company_id = self.env.user.company_id
if company_id.sms_active == True:
test = company_id.send_sms("", "Test from odex!")
test = test.text[:100].split("-")
error = company_id.get_error_response(test[1])
for rec in self:
templates = 'exp_transaction_documents.out_email'
template = self.env.ref(templates, False)
emails = rec.partner_id.email if rec.is_partner else rec.to_ids.mapped('email')
email_template = template.write(
{'email_to': emails})
template.with_context(lang=self.env.user.lang).send_mail(
rec.id, force_send=True, raise_exception=False)
def action_reject_outgoing(self):
name = 'default_outgoing_transaction_id'
return self.action_reject(name, self)
def action_return_outgoing(self):
name = 'default_outgoing_transaction_id'
return self.action_return_tran(name, self)
####################################################
# ORM Overrides methods
####################################################
@api.model
def create(self, vals):
seq = self.fetch_sequence()
if vals.get('preparation_id'):
code = self.env['cm.entity'].sudo().browse(vals['preparation_id']).code
x = seq.split('/')
sequence = "%s/%s/%s" % (x[0], code, x[1])
vals['name'] = sequence
else:
vals['name'] = seq
# vals['ean13'] = self.env['odex.barcode'].code128('OT', vals['name'], 'TR')
return super(OutgoingTransaction, self).create(vals)
#
# def unlink(self):
# if self.env.uid != 1:
# raise ValidationError(_("You can not delete transaction....."))
# return super(OutgoingTransaction, self).unlink()

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
class CMConfig(models.TransientModel):
_inherit = 'res.config.settings'
module_cm_hr_odex = fields.Boolean(string='Synchronization With HR ?', help='''
If checked, you will be able to sync employees, departments, job titles with Crosspondence Tracking System.
''')
module_cm_mail_odex = fields.Boolean(string='Convert Email Messages to Transactions', help='''
If checked, you can convert emails to Incoming Transactions.
''')
last_date_to_execute_transaction = fields.Boolean(string='Last Date To Execute Transaction', help='''
If checked, you add rank value to start date to get last date to execute Transaction.
''')
# ir.values is not exited in odoo 11 so i use ir.config_parameter instead of ir.value 15 Apr
@api.model
def get_values(self):
res = super(CMConfig, self).get_values()
res.update(
last_date_to_execute_transaction=self.env['ir.config_parameter'].sudo().get_param('exp_transaction_documents.last_date_to_execute_transaction'),
module_cm_mail_odex=self.env['ir.config_parameter'].sudo().get_param('exp_transaction_documents.module_cm_mail_odex'),
module_cm_hr_odex=self.env['ir.config_parameter'].sudo().get_param('exp_transaction_documents.module_cm_hr_odex')
)
return res
def set_values(self):
super(CMConfig, self).set_values()
self.env['ir.config_parameter'].sudo().set_param('exp_transaction_documents.last_date_to_execute_transaction', self.last_date_to_execute_transaction)
self.env['ir.config_parameter'].sudo().set_param('exp_transaction_documents.module_cm_mail_odex', self.module_cm_mail_odex)
self.env['ir.config_parameter'].sudo().set_param('exp_transaction_documents.module_cm_hr_odex', self.module_cm_hr_odex)
@api.model
def create(self, vals):
"""
create(vals) -> record
Override create to change last_date_to_execute_transaction_id value in cm.subject.type
"""
res = super(CMConfig, self).create(vals)
if res.last_date_to_execute_transaction:
for rec in self.env['cm.subject.type'].search([]):
rec.last_date_to_execute_transaction_id = True
return res

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