Merge branch 'staging' into 'development'

Staging

See merge request tabulasense/client_contracts!9
This commit is contained in:
Иван Широких 2020-01-28 12:37:37 +05:00
commit deade69594
12 changed files with 261 additions and 165 deletions

11
TODOLIST.md Normal file
View File

@ -0,0 +1,11 @@
# TODO LIST
## Features
## Fixes
- Change all `parents` to `genitive`
- Merge `document_type` and `template_type` in `res.partner.document.template`
- Change `annex_number` to `annex_counter`
## Big feature
- Separate XML actions that generates transient fields for all types of documents

View File

@ -19,6 +19,7 @@
"security/ir.model.access.csv", "security/ir.model.access.csv",
"views/res_partner_contract.xml", "views/res_partner_contract.xml",
"views/res_partner_contract_annex.xml", "views/res_partner_contract_annex.xml",
"views/res_partner_contract_field.xml",
"views/res_partner_document_template.xml", "views/res_partner_document_template.xml",
"views/res_partner.xml", "views/res_partner.xml",
"views/sale_order.xml", "views/sale_order.xml",

View File

@ -155,8 +155,8 @@ action = ctx
# Aliases # Aliases
self = record self = record
seller = self.contract_id.company_id seller = self.company_id
partner = self.contract_id.partner_id partner = self.partner_id
seller_bank = seller.bank_ids and seller.bank_ids[0] seller_bank = seller.bank_ids and seller.bank_ids[0]
seller_bank_name = seller_bank.bank_id.name + (" г. {city}".format(city=seller_bank.bank_id.city) if seller_bank.bank_id.city else "") seller_bank_name = seller_bank.bank_id.name + (" г. {city}".format(city=seller_bank.bank_id.city) if seller_bank.bank_id.city else "")
@ -191,7 +191,7 @@ ctx = {
"contract_number": self.contract_id.name, "contract_number": self.contract_id.name,
"annex_name": self.name, "annex_name": self.name,
"annex_number": self.number, "annex_number": self.counter,
"order_name": self.order_id.name, "order_name": self.order_id.name,
"order_date": "{} {} {}".format(order_date.day, months[order_date.month], order_date.year), "order_date": "{} {} {}".format(order_date.day, months[order_date.month], order_date.year),

View File

@ -6,8 +6,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 11.0-20191106\n" "Project-Id-Version: Odoo Server 11.0-20191106\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-01-21 14:27+0000\n" "POT-Creation-Date: 2020-01-22 11:29+0000\n"
"PO-Revision-Date: 2020-01-21 14:27+0000\n" "PO-Revision-Date: 2020-01-22 11:29+0000\n"
"Last-Translator: Stepan Savelyev\n" "Last-Translator: Stepan Savelyev\n"
"Language-Team: RYDLAB\n" "Language-Team: RYDLAB\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -42,14 +42,14 @@ msgid "3 stages"
msgstr "Оплата в 3 этапа" msgstr "Оплата в 3 этапа"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:28 #: code:addons/client_contracts/models/res_partner_document_template.py:31
#: selection:res.partner.document.template,document_type_name:0 #: selection:res.partner.document.template,document_type_name:0
#, python-format #, python-format
msgid "Act of Acceptance and Delivery" msgid "Act of Acceptance and Delivery"
msgstr "Акт сдачи-приемки" msgstr "Акт сдачи-приемки"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:27 #: code:addons/client_contracts/models/res_partner_document_template.py:30
#: selection:res.partner.document.template,document_type_name:0 #: selection:res.partner.document.template,document_type_name:0
#, python-format #, python-format
msgid "Act of Acceptance and Transfer" msgid "Act of Acceptance and Transfer"
@ -62,7 +62,7 @@ msgid "Actual Address"
msgstr "Актуальный адрес" msgstr "Актуальный адрес"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:18 #: code:addons/client_contracts/models/res_partner_document_template.py:21
#: selection:res.partner.document.template,document_type:0 #: selection:res.partner.document.template,document_type:0
#, python-format #, python-format
msgid "Addition" msgid "Addition"
@ -79,7 +79,7 @@ msgid "Amount Whole Part"
msgstr "Целая часть суммы" msgstr "Целая часть суммы"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:17 #: code:addons/client_contracts/models/res_partner_document_template.py:20
#: selection:res.partner.document.template,document_type:0 #: selection:res.partner.document.template,document_type:0
#: selection:res.partner.document.template,template_type:0 #: selection:res.partner.document.template,template_type:0
#, python-format #, python-format
@ -112,7 +112,7 @@ msgid "Annexes to this contract"
msgstr "Приложения к договору" msgstr "Приложения к договору"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:26 #: code:addons/client_contracts/models/res_partner_document_template.py:29
#: selection:res.partner.document.template,document_type_name:0 #: selection:res.partner.document.template,document_type_name:0
#, python-format #, python-format
msgid "Approval List" msgid "Approval List"
@ -147,7 +147,7 @@ msgid "Bank Payment Account"
msgstr "Расчётный счёт" msgstr "Расчётный счёт"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:24 #: code:addons/client_contracts/models/res_partner_document_template.py:27
#: selection:res.partner.document.template,document_type_name:0 #: selection:res.partner.document.template,document_type_name:0
#, python-format #, python-format
msgid "Bill" msgid "Bill"
@ -175,6 +175,7 @@ msgid "Closed"
msgstr "Закрыт" msgstr "Закрыт"
#. module: client_contracts #. module: client_contracts
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_annex_company_id
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_company_id #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_company_id
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_wizard_company_id #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_wizard_company_id
msgid "Company" msgid "Company"
@ -196,7 +197,7 @@ msgid "Contact"
msgstr "Контакт" msgstr "Контакт"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:16 #: code:addons/client_contracts/models/res_partner_document_template.py:19
#: model:ir.model,name:client_contracts.model_res_partner_contract #: model:ir.model,name:client_contracts.model_res_partner_contract
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_annex_contract_id #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_annex_contract_id
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_field_transient__contract_wizard_id #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_field_transient__contract_wizard_id
@ -233,6 +234,7 @@ msgid "Contract Field Transient"
msgstr "Фактическое поле договора" msgstr "Фактическое поле договора"
#. module: client_contracts #. module: client_contracts
#: model:ir.actions.act_window,name:client_contracts.res_partner_contract_field_action
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_wizard_transient_field_ids #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_wizard_transient_field_ids
msgid "Contract Fields" msgid "Contract Fields"
msgstr "Поля договора" msgstr "Поля договора"
@ -269,7 +271,7 @@ msgid "Counter for generate Annex name"
msgstr "Счётчик для генерации имени приложения к договору" msgstr "Счётчик для генерации имени приложения к договору"
#. module: client_contracts #. module: client_contracts
#: model:ir.model.fields,help:client_contracts.field_res_partner_contract_annex_number #: model:ir.model.fields,help:client_contracts.field_res_partner_contract_annex_counter
msgid "Counter of Contract Annexes" msgid "Counter of Contract Annexes"
msgstr "Счётчик приложений к договору" msgstr "Счётчик приложений к договору"
@ -303,6 +305,11 @@ msgstr "Создано"
msgid "Created on" msgid "Created on"
msgstr "Создан" msgstr "Создан"
#. module: client_contracts
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_annex_currency_id
msgid "Currency"
msgstr "Валюта"
#. module: client_contracts #. module: client_contracts
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_date_conclusion_fix #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_date_conclusion_fix
msgid "Date of manual conclusion" msgid "Date of manual conclusion"
@ -391,6 +398,11 @@ msgstr "Имя документа"
msgid "Document Template" msgid "Document Template"
msgstr "Шаблон документа" msgstr "Шаблон документа"
#. module: client_contracts
#: model:ir.actions.act_window,name:client_contracts.res_partner_document_template_action
msgid "Document Templates"
msgstr "Шаблоны документов"
#. module: client_contracts #. module: client_contracts
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_annex_design_doc_cost #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_annex_design_doc_cost
msgid "Documentation Design Cost" msgid "Documentation Design Cost"
@ -828,7 +840,7 @@ msgid "Print Form of Contract"
msgstr "Форма печати договора" msgstr "Форма печати договора"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_contract_annex.py:104 #: code:addons/client_contracts/models/res_partner_contract_annex.py:111
#, python-format #, python-format
msgid "Print Form of Contract Annex" msgid "Print Form of Contract Annex"
msgstr "Форма печати приложения к договору" msgstr "Форма печати приложения к договору"
@ -952,7 +964,7 @@ msgid "Sole Proprietor"
msgstr "Индивидуальный предприниматель" msgstr "Индивидуальный предприниматель"
#. module: client_contracts #. module: client_contracts
#: code:addons/client_contracts/models/res_partner_document_template.py:25 #: code:addons/client_contracts/models/res_partner_document_template.py:28
#: selection:res.partner.document.template,document_type_name:0 #: selection:res.partner.document.template,document_type_name:0
#, python-format #, python-format
msgid "Specification" msgid "Specification"
@ -1003,7 +1015,7 @@ msgid "Template Type"
msgstr "Тип шаблона" msgstr "Тип шаблона"
#. module: client_contracts #. module: client_contracts
#: model:ir.ui.menu,name:client_contracts.res_partner_menu_contracts_templates_ #: model:ir.ui.menu,name:client_contracts.res_partner_menu_contracts_templates
msgid "Templates" msgid "Templates"
msgstr "Шаблоны" msgstr "Шаблоны"
@ -1028,6 +1040,11 @@ msgstr "Transient Field Ids Hidden"
msgid "Type of document" msgid "Type of document"
msgstr "Вид документа" msgstr "Вид документа"
#. module: client_contracts
#: model:ir.model.fields,help:client_contracts.field_res_partner_contract_annex_currency_id
msgid "Utility field to express amount currency"
msgstr "Служебное поле, чтобы выразить суммы в валюте"
#. module: client_contracts #. module: client_contracts
#: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_field_transient_value #: model:ir.model.fields,field_description:client_contracts.field_res_partner_contract_field_transient_value
msgid "Value" msgid "Value"
@ -1068,23 +1085,13 @@ msgstr "Год"
msgid "i.e. Ural Bank for Reconstruction and Development" msgid "i.e. Ural Bank for Reconstruction and Development"
msgstr "напр. «Уральский банк реконструкции и развития»" msgstr "напр. «Уральский банк реконструкции и развития»"
#. module: client_contracts
#: model:ir.actions.act_window,name:client_contracts.res_partner_contract_field_action
msgid "res.partner.contract.field.action"
msgstr "res.partner.contract.field.action"
#. module: client_contracts #. module: client_contracts
#: model:ir.model,name:client_contracts.model_res_partner_contract_wizard #: model:ir.model,name:client_contracts.model_res_partner_contract_wizard
msgid "res.partner.contract.wizard" msgid "res.partner.contract.wizard"
msgstr "res.partner.contract.wizard" msgstr "res.partner.contract.wizard"
#. module: client_contracts #. module: client_contracts
#: model:ir.actions.act_window,name:client_contracts.res_partner_document_template_action #: code:addons/client_contracts/models/res_partner_contract_annex.py:64
msgid "res.partner.document.template.action"
msgstr "res.partner.document.template.action"
#. module: client_contracts
#: code:addons/client_contracts/models/res_partner_contract_annex.py:80
#, python-format #, python-format
msgid "{name} from {date}" msgid "{name} from {date}"
msgstr "{name} от {date}" msgstr "{name} от {date}"

View File

@ -1,7 +1,6 @@
import datetime import datetime
from odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from ..utils import MODULE_NAME from ..utils import MODULE_NAME
from ..utils.misc import Extension, IDocument from ..utils.misc import Extension, IDocument
@ -110,7 +109,11 @@ class PartnerContract(models.Model, IDocument, Extension):
def get_filename_by_document_template(self, document_template_id): def get_filename_by_document_template(self, document_template_id):
return _("{type} {number} from {date}").format( return _("{type} {number} from {date}").format(
type=_(dict(document_template_id._fields['document_type'].selection).get(document_template_id.document_type)), type=_(
dict(document_template_id._fields["document_type"].selection).get(
document_template_id.document_type
)
),
number=self.name, number=self.name,
date=self.get_date().strftime("%d.%m.%Y"), date=self.get_date().strftime("%d.%m.%Y"),
) )
@ -123,10 +126,9 @@ class PartnerContract(models.Model, IDocument, Extension):
""" """
date = self.date_conclusion_fix or self.date_conclusion date = self.date_conclusion_fix or self.date_conclusion
if date: if date:
date = datetime.datetime.strptime(date, DEFAULT_SERVER_DATE_FORMAT) date = self.parse_odoo_date(date)
else: else:
date = self.create_date date = self.parse_odoo_datetime(self.create_date)
date = datetime.datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT)
return date return date
def _(self, arg): def _(self, arg):

View File

@ -17,24 +17,26 @@ class ContractOrderAnnex(models.Model, IDocument, Extension):
contract_id = fields.Many2one( contract_id = fields.Many2one(
"res.partner.contract", string="Contract", readonly=True, "res.partner.contract", string="Contract", readonly=True,
) )
partner_id = fields.Many2one(related="contract_id.partner_id") company_id = fields.Many2one("res.partner", related="contract_id.company_id",)
partner_id = fields.Many2one("res.partner", related="contract_id.partner_id",)
order_id = fields.Many2one( order_id = fields.Many2one(
"sale.order", "sale.order",
string="Order", string="Order",
required=True,
help="Orders with this partner which are not uses in annexes yet", help="Orders with this partner which are not uses in annexes yet",
required=True,
) )
date_conclusion = fields.Date( date_conclusion = fields.Date(
string="Conclusion Date", default=fields.Date.today(), string="Conclusion Date", default=fields.Date.today(),
) )
number = fields.Integer(string="",help="Counter of Contract Annexes") counter = fields.Integer(string="", help="Counter of Contract Annexes",)
currency_id = fields.Many2one(related="company_id.currency_id", readonly=True,)
development_period = fields.Integer("Product Development Period (days)",) development_period = fields.Integer("Product Development Period (days)",)
design_cost = fields.Float(string="Design Cost",) design_cost = fields.Monetary(string="Design Cost",)
design_doc_period = fields.Integer(string="Documentation Design Period (days)",) design_doc_period = fields.Integer(string="Documentation Design Period (days)",)
design_doc_cost = fields.Float(string="Documentation Design Cost",) design_doc_cost = fields.Monetary(string="Documentation Design Cost",)
delivery_address = fields.Char(string="Delivery Address",) delivery_address = fields.Char(string="Delivery Address",)
delivery_period = fields.Integer(string="Delivery Period (days)") delivery_period = fields.Integer(string="Delivery Period (days)")
@ -43,12 +45,41 @@ class ContractOrderAnnex(models.Model, IDocument, Extension):
installation_period = fields.Integer(string="Installation Period (days)",) installation_period = fields.Integer(string="Installation Period (days)",)
installation_cost = fields.Integer(string="Installation Cost",) installation_cost = fields.Integer(string="Installation Cost",)
total_cost = fields.Float(string="Total Cost",) total_cost = fields.Monetary(string="Total Cost",)
payment_part_one = fields.Float(string="Payment 1 Part (%)", default=100) payment_part_one = fields.Float(string="Payment 1 Part (%)", default=100)
payment_part_two = fields.Float(string="Payment 2 Part (%)",) payment_part_two = fields.Float(string="Payment 2 Part (%)",)
payment_part_three = fields.Float(string="Payment 3 Part (%)",) payment_part_three = fields.Float(string="Payment 3 Part (%)",)
@api.multi
@api.depends("name")
def _compute_display_name(self):
for record in self:
record.display_name = "{} {}".format(
record.counter or record.contract_id.contract_annex_number, record.name
)
@api.depends("specification_name", "contract_id", "order_id")
def _compute_specification_name(self):
self.specification_name = _("{name} from {date}").format(
name="{}-{}".format(self.contract_id.name, self.order_id.name),
date=self.contract_id.get_date().strftime("%d.%m.%Y"),
)
@api.onchange("order_id")
def _domain_order_id(self):
"""Using domain function because of
simple domain does not working properly because of
contract_id is still False"""
return {
"domain": {
"order_id": [
("partner_id", "=", self.contract_id.partner_id.id),
("contract_annex_id", "=", False),
]
}
}
@api.onchange("order_id") @api.onchange("order_id")
def _onchange_order_id(self): def _onchange_order_id(self):
contract_number = self.contract_id.name contract_number = self.contract_id.name
@ -58,30 +89,6 @@ class ContractOrderAnnex(models.Model, IDocument, Extension):
contract=contract_number, order=order_number, contract=contract_number, order=order_number,
) )
# Compute domain for order_id because of bug with
# not working correctly domain in model
return {
"domain": {
"order_id": [
("partner_id", "=", self.contract_id.partner_id.id),
("contract_annex_id", "=", False),
]
}
}
@api.multi
@api.depends('name')
def _compute_display_name(self):
for record in self:
record.display_name = "{} {}".format(record.number or record.contract_id.contract_annex_number, record.name)
@api.depends('specification_name', 'contract_id', 'order_id')
def _compute_specification_name(self):
self.specification_name = _("{name} from {date}").format(
name="{}-{}".format(self.contract_id.name, self.order_id.name),
date=self.contract_id.get_date().strftime("%d.%m.%Y"),
)
@api.model @api.model
def create(self, values): def create(self, values):
record = super().create(values) record = super().create(values)
@ -90,8 +97,8 @@ class ContractOrderAnnex(models.Model, IDocument, Extension):
record.order_id.contract_annex_id = record.id record.order_id.contract_annex_id = record.id
# Counter # Counter
record.number = record.contract_id.contract_annex_number record.counter = record.contract_id.contract_annex_number
record.contract_id.contract_annex_number += 1 record.contract_id.contract_annex_number += 1 # TODO: should I use a sequence?
return record return record
@ -111,30 +118,41 @@ class ContractOrderAnnex(models.Model, IDocument, Extension):
} }
def get_name_by_document_template(self, document_template_id): def get_name_by_document_template(self, document_template_id):
return { return (
"specification": "{number} {name}", {
"approval_list": "{number}.1 {name}-1", "specification": "{counter} {name}",
"act_at": "{number}.2 {name}-2", "approval_list": "{counter}.1 {name}-1",
"act_ad": "{number}.3 {name}-3", "act_at": "{counter}.2 {name}-2",
}.get(document_template_id.document_type_name, "Unknown").format( "act_ad": "{counter}.3 {name}-3",
number=self.number, }
name=self.name, .get(document_template_id.document_type_name, "Unknown")
.format(counter=self.counter, name=self.name,)
) )
def get_filename_by_document_template(self, document_template_id): def get_filename_by_document_template(self, document_template_id):
return "{type}{name}".format( return "{type}{name}".format(
type=_(dict(document_template_id._fields['document_type'].selection).get(document_template_id.document_type)), type=_(
dict(document_template_id._fields["document_type"].selection).get(
document_template_id.document_type
)
),
name={ name={
"bill": "{number} {type} {name}", "bill": "{counter} {type} {name}",
"specification": "{number} {type} {name}", "specification": "{counter} {type} {name}",
"approval_list": "{number}.1 {type} {name}-1", "approval_list": "{counter}.1 {type} {name}-1",
"act_at": "{number}.2 {type} {name}-2", "act_at": "{counter}.2 {type} {name}-2",
"act_ad": "{number}.3 {type} {name}-3", "act_ad": "{counter}.3 {type} {name}-3",
}.get(document_template_id.document_type_name).format( }
number=self.number, .get(document_template_id.document_type_name)
type=_(dict(document_template_id._fields['document_type_name'].selection).get(document_template_id.document_type_name)), .format(
counter=self.counter,
type=_(
dict(
document_template_id._fields["document_type_name"].selection
).get(document_template_id.document_type_name)
),
name=self.name, name=self.name,
) ),
) )
def modf(self, arg): def modf(self, arg):

View File

@ -4,11 +4,14 @@ from odoo import _, fields, models
class DocumentTemplate(models.Model): class DocumentTemplate(models.Model):
_name = "res.partner.document.template" _name = "res.partner.document.template"
_description = "Document Template" _description = "Document Template"
_order = "template_type desc,company_type,sequence" _order = "company_type,document_type,sequence"
name = fields.Char() name = fields.Char()
attachment_id = fields.Many2one( attachment_id = fields.Many2one(
"ir.attachment", string="Template Attachment", ondelete="cascade", required=True, "ir.attachment",
string="Template Attachment",
ondelete="cascade",
required=True,
) )
document_type = fields.Selection( document_type = fields.Selection(
string="Type of document", string="Type of document",
@ -16,7 +19,7 @@ class DocumentTemplate(models.Model):
("contract", _("Contract")), ("contract", _("Contract")),
("annex", _("Annex")), ("annex", _("Annex")),
("addition", _("Addition")), ("addition", _("Addition")),
] ],
) )
document_type_name = fields.Selection( document_type_name = fields.Selection(
string="Document", string="Document",
@ -26,7 +29,7 @@ class DocumentTemplate(models.Model):
("approval_list", _("Approval List")), ("approval_list", _("Approval List")),
("act_at", _("Act of Acceptance and Transfer")), ("act_at", _("Act of Acceptance and Transfer")),
("act_ad", _("Act of Acceptance and Delivery")), ("act_ad", _("Act of Acceptance and Delivery")),
] ],
) )
company_type = fields.Selection( company_type = fields.Selection(
selection=[ selection=[

View File

@ -1,22 +1,26 @@
import inspect
import datetime as dt import datetime as dt
import inspect
from odoo import fields from odoo import fields
from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT from odoo.tools.misc import (DEFAULT_SERVER_DATE_FORMAT,
DEFAULT_SERVER_DATETIME_FORMAT)
class IDocument(object): class IDocument(object):
"""Class must be used as an interface for create new document based model""" """Class must be used as an interface for create new document based model"""
def get_name_by_document_template(self, document_template_id: fields.Many2one): def get_name_by_document_template(self, document_template_id: fields.Many2one):
raise NotImplementedError('Method {} is not implemented'.format(inspect.currentframe().f_code.co_name)) raise NotImplementedError(
"Method {} is not implemented".format(inspect.currentframe().f_code.co_name)
)
def get_filename_by_document_template(self, document_template_id: fields.Many2one): def get_filename_by_document_template(self, document_template_id: fields.Many2one):
raise NotImplementedError('Method {} is not implemented'.format(inspect.currentframe().f_code.co_name)) raise NotImplementedError(
"Method {} is not implemented".format(inspect.currentframe().f_code.co_name)
)
class Extension(object): class Extension(object):
def parse_odoo_date(self, date: str): def parse_odoo_date(self, date: str):
return dt.datetime.strptime(date, DEFAULT_SERVER_DATE_FORMAT) return dt.datetime.strptime(date, DEFAULT_SERVER_DATE_FORMAT)

View File

@ -3,7 +3,7 @@
<!-- res.partner.contract.field action window --> <!-- res.partner.contract.field action window -->
<record id="res_partner_contract_field_action" model="ir.actions.act_window"> <record id="res_partner_contract_field_action" model="ir.actions.act_window">
<field name="name">res.partner.contract.field.action</field> <field name="name">Contract Fields</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner.contract.field</field> <field name="res_model">res.partner.contract.field</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
@ -12,7 +12,7 @@
<!-- res.partner.document.template action window --> <!-- res.partner.document.template action window -->
<record id="res_partner_document_template_action" model="ir.actions.act_window"> <record id="res_partner_document_template_action" model="ir.actions.act_window">
<field name="name">res.partner.document.template.action</field> <field name="name">Document Templates</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner.document.template</field> <field name="res_model">res.partner.document.template</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
@ -87,7 +87,7 @@
parent="res_partner_menu_contracts" parent="res_partner_menu_contracts"
sequence="1"/> sequence="1"/>
<menuitem id="res_partner_menu_contracts_templates_" <menuitem id="res_partner_menu_contracts_templates"
name="Templates" name="Templates"
parent="res_partner_menu_contracts" parent="res_partner_menu_contracts"
action="res_partner_document_template_action" action="res_partner_document_template_action"

View File

@ -12,6 +12,7 @@
<sheet> <sheet>
<group name="options" invisible="1"> <group name="options" invisible="1">
<field name="display_name" invisible="1"/> <field name="display_name" invisible="1"/>
<field name="currency_id" invisible="1"/>
</group> </group>
<group name="info" string="Info"> <group name="info" string="Info">
<field name="name" placeholder="Leave empty for compute"/> <field name="name" placeholder="Leave empty for compute"/>

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<data>
<!-- res.partner.contract.field tree view -->
<record id="res_partner_contract_field_view_tree" model="ir.ui.view">
<field name="name">res.partner.contract.field.view.tree</field>
<field name="model">res.partner.contract.field</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="description"/>
<field name="technical_name"/>
<field name="visible"/>
</tree>
</field>
</record>
</data>
</odoo>

View File

@ -1,5 +1,4 @@
import base64 import base64
import logging
from odoo import api, fields, models from odoo import api, fields, models
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
@ -7,35 +6,32 @@ from odoo.exceptions import ValidationError
from ..utils import MODULE_NAME from ..utils import MODULE_NAME
from ..utils.docxtpl import get_document_from_values_stream from ..utils.docxtpl import get_document_from_values_stream
_logger = logging.getLogger(__name__)
class ContractWizard(models.TransientModel): class ContractWizard(models.TransientModel):
_name = "res.partner.contract.wizard" _name = "res.partner.contract.wizard"
def _get_default_partner(self): def _default_target(self):
current_id = self.env.context.get("active_id") return "{model},{target_id}".format(
partner_id = self.env[self.active_model].browse(current_id).partner_id model=self.active_model, target_id=int(self.env.context.get("self_id"))
return partner_id )
def _get_default_template(self): def _default_document_template(self):
partner_id = self._get_default_partner() return self.env["res.partner.document.template"].search(self._get_template_domain(), limit=1)
def _get_template_domain(self):
template_type = { template_type = {
"res.partner.contract": "contract", "res.partner.contract": "contract",
"res.partner.contract.annex": "annex", "res.partner.contract.annex": "annex",
}.get(self.active_model, False) }.get(self.active_model, False)
company_type = ( company_type = (
partner_id.company_form if partner_id.is_company else "person" self.partner_id.company_form if self.partner_id.is_company else "person"
) )
document_template_domain = [ document_template_domain = [
("template_type", "=", template_type), ("template_type", "=", template_type),
("company_type", "=", company_type), ("company_type", "=", company_type),
] ]
return document_template_domain
return self.env["res.partner.document.template"].search(
document_template_domain, limit=1
)
target = fields.Reference( target = fields.Reference(
selection=[ selection=[
@ -43,13 +39,23 @@ class ContractWizard(models.TransientModel):
("res.partner.contract.annex", "Contract Annex"), ("res.partner.contract.annex", "Contract Annex"),
], ],
string="Target", string="Target",
default=_default_target,
)
company_id = fields.Many2one(
"res.partner", string="Company", compute="_compute_company_id",
)
partner_id = fields.Many2one(
"res.partner", string="Partner", compute="_compute_partner_id",
)
document_name = fields.Char(
string="Document Name", compute="_compute_document_name"
) )
company_id = fields.Many2one("res.partner", string="Company")
partner_id = fields.Many2one("res.partner", string="Partner", default=_get_default_partner)
document_template = fields.Many2one( document_template = fields.Many2one(
"res.partner.document.template", string="Document Template", default=_get_default_template, "res.partner.document.template",
string="Document Template",
default=_default_document_template,
readonly=False,
) )
document_name = fields.Char(string="Document Name", compute='_compute_document_name')
transient_field_ids = fields.One2many( transient_field_ids = fields.One2many(
"res.partner.contract.field.transient", "res.partner.contract.field.transient",
"_contract_wizard_id", "_contract_wizard_id",
@ -59,14 +65,34 @@ class ContractWizard(models.TransientModel):
"res.partner.contract.field.transient", "_contract_wizard_id", "res.partner.contract.field.transient", "_contract_wizard_id",
) )
@api.depends("company_id", "target")
def _compute_company_id(self):
if self.target:
self.company_id = self.target.company_id
@api.depends("partner_id", "target")
def _compute_partner_id(self):
if self.target:
self.partner_id = self.target.partner_id
@api.depends("document_name", "document_template", "target")
def _compute_document_name(self):
self.document_name = self.target.get_name_by_document_template(
self.document_template
)
@api.constrains("document_template") @api.constrains("document_template")
def _check_document_template(self): def _check_document_template(self):
if not self.document_template: if not self.document_template:
raise ValidationError("You did not set up the template...") raise ValidationError("You did not set up the template...")
@api.depends('document_name', 'document_template', 'target') @api.onchange('document_template')
def _compute_document_name(self): def _domain_document_template(self):
self.document_name = self.target.get_name_by_document_template(self.document_template) return {
"domain": {
"document_template": self._get_template_domain(),
}
}
@api.onchange("document_template") @api.onchange("document_template")
def _onchange_document_template(self): def _onchange_document_template(self):
@ -79,25 +105,6 @@ class ContractWizard(models.TransientModel):
[("technical_name", "=", technical_name),] [("technical_name", "=", technical_name),]
) )
# A record is the model called from (manually set with context)
self.target = "{model},{target_id}".format(
model=self.active_model, target_id=int(self.env.context.get("self_id"))
)
# Check for model and get this meta fields
company_id = (
self.target.company_id
if hasattr(self.target, "company_id")
else self.target.contract_id.company_id
)
partner_id = (
self.target.partner_id
if hasattr(self.target, "partner_id")
else self.target.contract_id.partner_id
)
self.company_id = company_id
self.partner_id = partner_id
model_to_action = { model_to_action = {
"res.partner.contract": "action_get_contract_context", "res.partner.contract": "action_get_contract_context",
"res.partner.contract.annex": "action_get_annex_context", "res.partner.contract.annex": "action_get_annex_context",
@ -134,41 +141,19 @@ class ContractWizard(models.TransientModel):
self.transient_field_ids - self.transient_field_ids_hidden self.transient_field_ids - self.transient_field_ids_hidden
) )
# TODO: remove replicate of code # Other
template_type = {
"res.partner.contract": "contract",
"res.partner.contract.annex": "annex",
}.get(self.active_model, False)
company_type = (
self.partner_id.company_form if self.partner_id.is_company else "person"
)
return {"domain": {"document_template": [("template_type", "=", template_type),("company_type", "=", company_type),],}}
@api.multi @api.multi
def get_docx_contract(self): def get_docx_contract(self):
template = self.document_template.attachment_id template = self.document_template.attachment_id
template_path = template._full_path(template.store_fname)
path_to_template = template._full_path(template.store_fname) payload = self.payload()
binary_data = get_document_from_values_stream(template_path, payload).read()
fields = {
transient_field.technical_name: transient_field.value
for transient_field in (
self.transient_field_ids + self.transient_field_ids_hidden
)
if transient_field.technical_name and transient_field.value
}
if self.target._name == "res.partner.contract.annex":
fields.update({
"annex_name": self.document_name,
"specification_name": self.target.specification_name,
})
binary_data = get_document_from_values_stream(path_to_template, fields).read()
encoded_data = base64.b64encode(binary_data) encoded_data = base64.b64encode(binary_data)
attachment_name = self.target.get_filename_by_document_template(self.document_template) or "Unknown" get_fn = self.target.get_filename_by_document_template
attachment_name = "{}.docx".format(attachment_name) attachment_name = "{}.docx".format(get_fn(self.document_template or "Unknown"))
document_as_attachment = self.env["ir.attachment"].create( document_as_attachment = self.env["ir.attachment"].create(
{ {
@ -179,7 +164,51 @@ class ContractWizard(models.TransientModel):
} }
) )
# Send message with attachment to a mail.thread of the company return self.afterload(document_as_attachment)
def payload(self):
# Collect fields into a key-value structure
fields = {
transient_field.technical_name: transient_field.value
for transient_field in (
self.transient_field_ids + self.transient_field_ids_hidden
)
if transient_field.technical_name and transient_field.value
}
# Extend with special case
if self.target._name == "res.partner.contract.annex":
fields.update(
{
"annex_name": self.document_name,
"specification_name": self.target.specification_name,
}
)
# Extend with order product lines
if hasattr(self.target, "order_id") and self.target.order_id.order_line:
def number_generator(n=1):
while (True):
yield n
n += 1
counter = number_generator()
fields.update(
{
"order_products": [
{
"number": next(counter),
"label": item.product_id.name,
"count": item.product_uom_qty,
"unit": item.product_uom.name,
"cost": item.price_unit,
"amount": item.price_subtotal,
} for item in self.target.order_id.order_line or []
]
}
)
return fields
def afterload(self, result):
res_id = self.target.id res_id = self.target.id
if hasattr(self.target, "contract_id"): if hasattr(self.target, "contract_id"):
res_id = self.target.contract_id.id res_id = self.target.contract_id.id
@ -189,11 +218,11 @@ class ContractWizard(models.TransientModel):
"model": "res.partner.contract", "model": "res.partner.contract",
"res_id": res_id, "res_id": res_id,
"message_type": "comment", "message_type": "comment",
"attachment_ids": [(4, document_as_attachment.id, False)], "attachment_ids": [(4, result.id, False)],
} }
) )
return document_as_attachment return result
@property @property
def active_model(self): def active_model(self):