1
0
forked from Rudoo/L10n_ru

Российская локализация для v18

This commit is contained in:
Sergey Korobkov 2025-01-17 20:45:47 +03:00
parent e07335deaa
commit 2c894fe74b
200 changed files with 13131 additions and 0 deletions

14
l10n_ru_act_rev/README.md Normal file
View File

@ -0,0 +1,14 @@
# Российская локализация - Акт сверки
name: l10n_ru_act_rev
## Описание
Добавление печатной формы акт сверки из контактов, с помощью которой можно легко отслеживать дебеторские и кредиторские проводки с клиентами.
### Для печати:
1. Выбираем меню Контакты - карточку конкретного партнера - Действия - "Печать акт сверки";
2. В визарде выбираем:
2.1. Компанию (для которой нужна сверка с выбранным контактом);
2.2. Период сверки;
2.3. Цель (один из режимом: все проведенные проводки или все проводки, включая черновики);
3. Кнопка "Печать"

View File

@ -0,0 +1,4 @@
from . import models
from . import report
from . import wizard
from . import controllers

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
{
'name': "Российская локализация - Акт сверки",
'summary': """
Добавление отчета акт сверки""",
'description': """
Добавление формы акт сверки, находящегося в контактах, с помощью которого можно легко отслеживать дебеторские и кредиторские проводки с клиентами.
Для печати:
1. Выбираем меню Контакты - конкретного партнера - Действия - "Печать акт сверки";
2. В визарде выбираем:
2.1. Компанию (для которой нужна сверка с выбранным контактом);
2.2. Период сверки;
2.3. Цель (один из режимом: все проведенные проводки или все проводки, включая черновики);
3. Кнопка "Печать"
""",
'author': "MK.Lab",
'website': "https://www.inf-centre.ru/",
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
"depends": ["account", "portal", "website", 'contacts', "l10n_ru_doc", 'l10n_ru_contract', 'l10n_ru_base'],
"data": [
"security/ir.model.access.csv",
"wizard/general_ledger_wizard_view.xml",
"report/layouts.xml",
"report/general_ledger.xml",
"views/account_account_views.xml",
"views/report_general_ledger.xml",
"views/portal_templates.xml",
],
"installable": True,
"application": True,
"auto_install": False,
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}

View File

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

View File

@ -0,0 +1,98 @@
from odoo import http
from odoo.http import request
from datetime import datetime, date
import werkzeug
class ActRevise(http.Controller):
@http.route(['/my/act_revise/<string:act>'], type='http', auth="public", website=True)
def print_report(self):
partner = request.env.user.partner_id.parent_id.id
partner_name = request.env.user.partner_id.parent_id.name
if not partner:
partner = request.env.user.partner_id.id
partner_name = request.env.user.partner_id.name
company = request.env.user.company_id.id
# company_name = request.env.user.company_id.name
# today = date.today()
# d1 = today.strftime("%d.%m.%y")
#
# new_url=str('AC ' + company +' - ' + partner + ' ' + d1)
# if request.httprequest.full_path == '/my/act_revise/a?':
# return werkzeug.utils.redirect('/my/act_revise/%s' % new_url)
wizard_data = {
"target_move": "posted",
"hide_account_at_0": True,
"foreign_currency": True,
"company_id": company,
"partner_ids": [partner],
"show_cost_center": True,
"centralize": True
}
wizard_record = request.env['general.ledger.act_revise.wizard'].sudo().create(wizard_data)
action_read = request.env.ref('l10n_ru_act_rev.action_general_ledger_wizard').sudo().read()
if action_read:
action = action_read[0]
action['res_id'] = wizard_record.id
action['context'] = dict(request.env.context)
return request.redirect('/web#action=' + str(action['id']) + '&id=' + str(wizard_record.id) + '&view_type=form')
else:
return request.make_response("Error: Action not found.",[('Content-Type', 'text/plain')],400)
# @http.route(['/my/act_revise/<string:act>'], type='http', auth="public", website=True)
# def print_report(self):
# partner = request.env.user.partner_id.parent_id.id
# partner_name = request.env.user.partner_id.parent_id.name
# if not partner:
# partner = request.env.user.partner_id.id
# partner_name = request.env.user.partner_id.name
# company = request.env.user.company_id.id
# company_name = request.env.user.company_id.name
# today = date.today()
# d1 = today.strftime("%d.%m.%y")
# # new_url = str('Акт Сверки ' + d1 + ' ' + company_name + '_' + partner_name)
# # new_url=str('AC ' + company +' - ' + partner + ' ' + d1)
# # if request.httprequest.full_path == '/my/act_revise/a?':
# # return werkzeug.utils.redirect('/my/act_revise/%s' % new_url)
# wizard_data = {"target_move": "posted",
# "hide_account_at_0": True,
# "foreign_currency": True,
# #"show_analytic_tags": True,
# "company_id": company,
# "partner_ids": [partner],
# #"show_partner_details": True,
# "show_cost_center": True,
# "centralize": True}
# t = request.env['general.ledger.act_revise.wizard'].sudo().create(wizard_data)
# data = t._prepare_report_general_ledger()
# name = t.get_report_filename()
# report_name = name.encode('cp1251')
# pdf, _ = request.env['ir.actions.report']._render_qweb_pdf(
# 'act_revise.action_print_report_general_ledger_qweb', res_ids=t.id, data=data)
# pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf)), ]
# return request.make_response(pdf, headers=pdfhttpheaders)
@http.route(['/my/act_revise_contact/<string:act>'], type='http', auth="public", website=True)
def print_report_contact(self, date_to, date_from, target_move, company, partner):
partner_id = int(partner) or 'default_partner_value'
company_id = int(company)
wizard_data = {"date_to": date_to,
"date_from": date_from,
"target_move": target_move,
"hide_account_at_0": True,
"foreign_currency": True,
#"show_analytic_tags": True,
"company_id": company_id,
"partner_ids": [partner_id],
#"show_partner_details": True,
"show_cost_center": True,
"centralize": True}
t = request.env['general.ledger.act_revise.wizard'].sudo().create(wizard_data)
data = t._prepare_report_general_ledger()
pdf, _ = request.env['ir.actions.report']._render_qweb_pdf(
'l10n_ru_act_rev.action_print_report_general_ledger_qweb', res_ids=t.id, data=data)
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf)), ]
return request.make_response(pdf, headers=pdfhttpheaders)

View File

@ -0,0 +1,30 @@
<odoo>
<data>
<!--
<record id="object0" model="act_revise.act_revise">
<field name="name">Object 0</field>
<field name="value">0</field>
</record>
<record id="object1" model="act_revise.act_revise">
<field name="name">Object 1</field>
<field name="value">10</field>
</record>
<record id="object2" model="act_revise.act_revise">
<field name="name">Object 2</field>
<field name="value">20</field>
</record>
<record id="object3" model="act_revise.act_revise">
<field name="name">Object 3</field>
<field name="value">30</field>
</record>
<record id="object4" model="act_revise.act_revise">
<field name="name">Object 4</field>
<field name="value">40</field>
</record>
-->
</data>
</odoo>

View File

@ -0,0 +1,2 @@
from . import account_account
from . import ir_actions_report

View File

@ -0,0 +1,7 @@
from odoo import fields, models, _
class AccountAccount(models.Model):
_inherit = "account.account"
centralized = fields.Boolean(_("Централизованно"))

View File

@ -0,0 +1,10 @@
from odoo import api, models
class IrActionsReport(models.Model):
_inherit = "ir.actions.report"
@api.model
def _prepare_account_financial_report_context(self, data):
lang = data and data.get("account_financial_report_lang") or ""
return dict(self.env.context or {}, lang=lang) if lang else False

View File

@ -0,0 +1,2 @@
from . import general_ledger

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,286 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<template id="l10n_ru_act_rev.general_ledger">
<t t-call="l10n_ru_act_rev.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="l10n_ru_act_rev.internal_layout">
<t t-call="l10n_ru_act_rev.report_general_ledger_base" />
</t>
</t>
</t>
</template>
<template id="l10n_ru_act_rev.report_general_ledger_base">
<t t-set="foreign_currency" t-value="foreign_currency" />
<t t-set="filter_partner_ids" t-value="filter_partner_ids" />
<div class="page">
<style type="text/css">
body { background: #ffffff; margin: 0; font-family: Arial; font-size: 10pt; font-style: normal; text-align: center;}
tbody, table, tr, td, th { border: none;}
tr.R0{ height: 15px; border: border: none; }
tr.R0 td.R0t{text-align: center; font-family: Tahoma; font-size: 14pt; font-weight: bold;}
tr.R0 td.R0j{text-align: justify }
tr.R0 td.R0l{text-align: left}
tr.R0 td.R0lb{font-weight: bold; text-align: left}
tr.R0 td.R0lbt{font-weight: bold; text-align: left; border-top: #000000 1px solid;}
tr.R0 td.R0lbox{text-align: left; border-left: #000000 1px solid; border-top: #000000 1px solid;
border-right: #000000 1px solid; border-bottom: #000000 1px solid; vertical-align: middle;}
tr.R0 td.R0cbox{text-align: center; border-left: #000000 1px solid; border-top: #000000 1px solid;
border-right: #000000 1px solid; border-bottom: #000000 1px solid; vertical-align: middle;}
tr.R0 td.R0lbbox{font-weight: bold; text-align: left; border-left: #000000 1px solid; border-top:
#000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px solid; vertical-align: middle;}
tr.R0 td.R0rbox{text-align: right; border-left: #000000 1px solid; border-top: #000000 1px solid;
border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
tr.R0 td.R0rbbox{font-weight: bold; text-align: right; border-left: #000000 1px solid; border-top:
#000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
tr.R0 td.Rcbox{text-align: center; border-left: #000000 1px solid; border-top: #000000 1px solid;
border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
tr.R0 td.R0cbbox{font-weight: bold; text-align: center; border-left: #000000 1px solid; border-top:
#000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
tr.R0 td.R0lboxf{background: #FFFFCC;text-align: left; border-left: #000000 1px solid; border-top:
#000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
tr.R0 td.R0lbboxf{background: #FFFFCC;font-weight: bold; text-align: left; border-left: #000000 1px
solid; border-top: #000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px
solid;}
tr.R0 td.R0rboxf{background: #FFFFCC;text-align: right; border-left: #000000 1px solid; border-top:
#000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
tr.R0 td.R0rbboxf{background: #FFFFCC;font-weight: bold; text-align: right; border-left: #000000 1px
solid; border-top: #000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px
solid;}
tr.R0 td.Rcboxf{background: #FFFFCC;text-align: center; border-left: #000000 1px solid; border-top:
#000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
tr.R0 td.R0cbboxf{background: #FFFFCC;font-weight: bold; text-align: center; border-left: #000000 1px
solid; border-top: #000000 1px solid; border-right: #000000 1px solid; border-bottom: #000000 1px
solid; vertical-align: middle;}
tr.R0 td.R0print span img { position: absolute; width: 140px; top: -80px; left: -350px; }
tr.R0 td.R0print { overflow: visible; }
tr.R0 td.R0print span { position: relative; }
tr.R0 td.R0print{ text-align: left; }
tr.R0 td.R0fas { font-family: Arial; font-size: 7pt; font-style: normal; text-align: right; border-bottom: #000000 1px solid; }
tr.R0 td.R0fas { overflow: visible; }
tr.R0 td.R0fas span { position: relative; }
tr.R0 td.R0fas span img { position: absolute; width: 100px; top: -25px; left: 200px;}
</style>
<t t-set="lines" t-value='[]' />
<t t-set="total_debit_init_all" t-value='0.0'/>
<t t-set="total_credit_init_all" t-value='0.0'/>
<t t-foreach="general_ledger" t-as="account">
<t t-if="'list_grouped' in account">
<t t-foreach="account['list_grouped']" t-as="group_item">
<t
t-set="misc_domain"
t-value="[('account_id', '=', account['id']),('date', '&lt;', date_from)]"
/>
<t
t-set="misc_grouped_domain"
t-value="[('partner_id', '=', group_item['id'])]"
t-if="'partners' in account"
/>
<t t-set="misc_grouped_domain" t-value="[]" t-else="" />
<t t-set="debit_domain" t-value="[('debit', '&lt;&gt;', 0)]" />
<span
t-att-domain="misc_domain+debit_domain+misc_grouped_domain"
res-model="account.move.line"
>
<t t-set="total_debit_init" t-value="group_item['init_bal']['debit']" />
<t t-set="total_debit_init_all" t-value='total_debit_init_all+total_debit_init'/>
</span>
<t t-set="credit_domain" t-value="[('credit', '&lt;&gt;', 0)]" />
<span
t-att-domain="misc_domain+credit_domain+misc_grouped_domain"
res-model="account.move.line"
>
<t t-set="total_credit_init" t-value="group_item['init_bal']['credit']" />
<t t-set="total_credit_init_all" t-value='total_credit_init_all+total_credit_init'/>
</span>
<t t-set="lines" t-value='lines+group_item["move_lines"]' />
</t>
</t>
</t>
<t t-set="lines" t-value='o.sorted_lines(lines)'/>
<t t-set="total_debit" t-value='0.0' />
<t t-set="total_credit" t-value='0.0' />
<TABLE CELLSPACING="0">
<COL WIDTH="12%"/>
<COL WIDTH="14%"/>
<COL WIDTH="12%"/>
<COL WIDTH="12%"/>
<COL WIDTH="12%"/>
<COL WIDTH="12%"/>
<COL WIDTH="14%"/>
<COL WIDTH="12%"/>
<tr CLASS="R0">
<td colspan="8" CLASS="R0t">Акт сверки</td>
</tr>
<tr CLASS="R0">
<td colspan="8">взаимных расчетов за период: <t t-esc="o.get_data_format(date_from) or ''"/> - <t t-esc="o.get_data_format(date_to) or ''"/></td>
</tr>
<t t-set="pid" t-value="o.partner_ids[0].parent_id or o.partner_ids[0]"/>
<tr CLASS="R0">
<td colspan="8">между: <t t-esc="company_name"/> и <t t-esc="pid.name"/></td>
</tr>
<tr CLASS="R0">
<td colspan="8"><t t-if="o.get_contract()">по договору №<t t-esc="o.get_contract().name"/> от <t t-esc="o.get_contract().date_start"/></t></td>
</tr>
<tr CLASS="R0">
<td colspan="8" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="8" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="8" CLASS="R0j">Мы, нижеподписавшиеся, <t t-esc="company_name"/> с одной стороны, и <t t-esc="pid.name"/>, с другой стороны, составили данный акт сверки в том, что, состояние взаимных расчетов по данным учета следующее:</td>
</tr>
<tr CLASS="R0">
<td colspan="8" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="4" CLASS="R0lbox">По данным <t t-esc="company_name"/>, руб</td>
<td colspan="4" CLASS="R0lbox">По данным <t t-esc="pid.name"/>, руб</td>
</tr>
<tr CLASS="R0">
<td CLASS="R0cbbox">Дата</td>
<td CLASS="R0cbbox">Документ</td>
<td CLASS="R0cbbox">Дебет</td>
<td CLASS="R0cbbox">Кредит</td>
<td CLASS="R0cbbox">Дата</td>
<td CLASS="R0cbbox">Документ</td>
<td CLASS="R0cbbox">Дебет</td>
<td CLASS="R0cbbox">Кредит</td>
</tr>
<tr CLASS="R0">
<t
t-set="misc_domain"
t-value="[('account_id', '=', account['id']),('date', '&lt;', date_from)]"
/>
<t
t-set="misc_grouped_domain"
t-value="[('partner_id', '=', group_item['id'])]"
t-if="'partners' in account"
/>
<t t-set="misc_grouped_domain" t-value="[]" t-else="" />
<td colspan="2" CLASS="R0lbox">Сальдо начальное</td>
<td CLASS="R0cbox"><span t-esc="'%0.2f'%(total_debit_init_all)"/></td>
<td CLASS="R0cbox"><span t-esc="'%0.2f'%(total_credit_init_all)"/></td>
<td colspan="2" CLASS="R0lbox">Сальдо начальное</td>
<td CLASS="R0cbox"></td>
<td CLASS="R0cbox"></td>
</tr>
<tr CLASS="R0" t-foreach="lines" t-as="line">
<t t-set="total_debit" t-value="total_debit+line['debit']" />
<t t-set="total_credit" t-value="total_credit+line['credit']" />
<td CLASS="R0cbox"><t t-esc="o.get_data_format(line['date']) or ''"/></td>
<td CLASS="R0lbox"><t t-raw="line['entry']"/></td>
<td CLASS="R0cbox"><t t-raw="'%0.2f'%(line['debit'])"/></td>
<td CLASS="R0cbox"><t t-raw="'%0.2f'%(line['credit'])"/></td>
<td CLASS="R0cbox"></td>
<td CLASS="R0lbox"></td>
<td CLASS="R0cbox"></td>
<td CLASS="R0cbox"></td>
</tr>
<tr CLASS="R0">
<td colspan="2" CLASS="R0lbbox">Обороты за период</td>
<td CLASS="R0cbbox"><t t-raw="'%0.2f'%(total_debit)"/>
</td>
<td CLASS="R0cbbox"><t t-raw="'%0.2f'%(total_credit)"/>
</td>
<td colspan="2" CLASS="R0lbbox">Обороты за период</td>
<td CLASS="R0cbbox"></td>
<td CLASS="R0cbbox"></td>
</tr>
<tr CLASS="R0">
<td colspan="2" CLASS="R0lbbox">Сальдо конечное</td>
<td CLASS="R0cbbox"><t t-raw="'%0.2f'%(total_debit_init_all+total_debit)"/></td>
<td CLASS="R0cbbox"><t t-raw="'%0.2f'%(total_credit_init_all+total_credit)"/></td>
<td colspan="2" CLASS="R0lbbox">Сальдо конечное</td>
<td CLASS="R0cbbox"></td>
<td CLASS="R0cbbox"></td>
</tr>
<t t-set="total_balance_fin" t-value='(total_debit_init_all+total_debit)-(total_credit_init_all+total_credit)'/>
<tr CLASS="R0">
<td colspan="8" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="4" CLASS="R0l">По данным <t t-esc="company_name"/></td>
<td colspan="4" CLASS="R0l">По данным <t t-esc="pid.name"/></td>
</tr>
<tr CLASS="R0">
<td colspan="4" CLASS="R0lb">на <t t-esc="o.get_data_format(date_to) or ''"/> задолженность в пользу <t t-if="total_balance_fin>=0"><t t-esc="company_name"/></t><t t-else=""><t t-esc="pid.name"/></t> <t t-esc="'%0.2f'%(abs(total_balance_fin))"/> руб. (<t t-esc="o.rubles(abs(total_balance_fin)) or ''"/>)"
</td>
<td colspan="4" CLASS="R0lb">на <t t-esc="o.get_data_format(date_to) or ''"/> задолженность в пользу <t t-if="total_balance_fin>=0"><t t-esc="company_name"/></t><t t-else=""><t t-esc="pid.name"/></t> <t t-esc="'%0.2f'%(abs(total_balance_fin))"/> руб. (<t t-esc="o.rubles(abs(total_balance_fin)) or ''"/>)"
</td>
</tr>
<tr CLASS="R0">
<td colspan="8" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="4" CLASS="R0l">От <t t-esc="company_name"/></td>
<td colspan="4" CLASS="R0l">От <t t-esc="pid.name"/></td>
</tr>
<tr CLASS="R0">
<td colspan="8" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="4" CLASS="R0lb"><t t-esc="o.company_id.chief_id.function or ''"/></td>
<td colspan="4" CLASS="R0lb">Директор</td>
</tr>
<tr CLASS="R0">
<td colspan="8" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="2" CLASS="R0fas"><SPAN><t t-raw="o.company_id.print_facsimile and o.img(o.company_id.chief_id.facsimile) or ''"/></SPAN></td>
<td colspan="2" CLASS="R0lb"><t t-esc="o.initials(o.company_id.chief_id.name) or ''"/></td>
<td colspan="2" CLASS="R0lb"></td>
<td colspan="2" CLASS="R0lb"></td>
</tr>
<tr CLASS="R0">
<td colspan="2" CLASS="R0lbt">М.П.</td>
<td colspan="2" CLASS="R0print"><SPAN><t t-raw="o.company_id.print_stamp and (o.company_id.print_anywhere or context.get('mark_invoice_as_sent', False)) and o.img(o.company_id.stamp) or ''"/></SPAN></td>
<td colspan="2" CLASS="R0lbt">М.П.</td>
<td colspan="2" CLASS="R0lb"></td>
</tr>
</TABLE>
</div>
</template>
<!-- <report-->
<!-- id="action_print_report_general_ledger_qweb"-->
<!-- model="general.ledger.report.wizard"-->
<!-- string="Акт сверки"-->
<!-- report_type="qweb-pdf"-->
<!-- menu="False"-->
<!-- name="act_revise.general_ledger"-->
<!-- file="act_revise.general_ledger"-->
<!-- print_report_name=" 'Акт сверки - %s' %s (object.get_report_filename()) or ''"-->
<!-- />-->
<record id="action_print_report_general_ledger_qweb" model="ir.actions.report">
<field name="name">Акт сверки</field>
<field name="model">general.ledger.act_revise.wizard</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_ru_act_rev.general_ledger</field>
<field name="report_file">l10n_ru_act_rev.general_ledger</field>
<field name="print_report_name">'Акт сверки - %s' % (object.get_report_filename() or '')</field>
<field name="binding_model_id" ref="model_general_ledger_act_revise_wizard"/>
</record>
<record id="report_qweb_paperformat" model="report.paperformat">
<field name="name">Account financial report qweb paperformat</field>
<field name="default" eval="True" />
<field name="format">custom</field>
<field name="page_height">297</field>
<field name="page_width">210</field>
<field name="orientation">Landscape</field>
<field name="margin_top">12</field>
<field name="margin_bottom">8</field>
<field name="margin_left">5</field>
<field name="margin_right">5</field>
<field name="header_line" eval="False" />
<field name="header_spacing">10</field>
<field name="dpi">110</field>
</record>
<record id="action_print_report_general_ledger_qweb" model="ir.actions.report">
<field name="paperformat_id" ref="report_qweb_paperformat" />
</record>
</odoo>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<template id="l10n_ru_act_rev.html_container">
<t t-set="body_classname" t-value="'container'" />
<t t-call="web.report_layout">
<t t-out="0" />
</t>
</template>
<template id="l10n_ru_act_rev.internal_layout">
<div class="article o_account_financial_reports_page">
<t t-out="0" />
</div>
<div class="footer">
<div class="row">
<div class="col-6 custom_footer">
<span
t-esc="context_timestamp(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M')"
/>
</div>
<div class="col-6 text-right custom_footer">
<ul class="list-inline">
<li class="list-inline-item">
<span class="page" />
</li>
<li class="list-inline-item">/</li>
<li class="list-inline-item">
<span class="topage" />
</li>
</ul>
</div>
</div>
</div>
</template>
</odoo>

View File

@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_general_ledger_act_revise_wizard,general.ledger.act_revise.wizard,model_general_ledger_act_revise_wizard,base.group_user,1,1,1,1
access_general_ledger_act_revise_wizard_portal,general.ledger.act_revise.wizard.portal,model_general_ledger_act_revise_wizard,base.group_portal,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_general_ledger_act_revise_wizard general.ledger.act_revise.wizard model_general_ledger_act_revise_wizard base.group_user 1 1 1 1
3 access_general_ledger_act_revise_wizard_portal general.ledger.act_revise.wizard.portal model_general_ledger_act_revise_wizard base.group_portal 1 1 1 1

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_account_form" model="ir.ui.view">
<field name="name">account.account.form.inherit</field>
<field name="inherit_id" ref="account.view_account_form" />
<field name="model">account.account</field>
<field name="type">form</field>
<field name="arch" type="xml">
<field name="deprecated" position="after">
<field name="centralized" />
</field>
</field>
</record>
</odoo>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_my_home_act_revise" name="Portal My Home : act revise" inherit_id="portal.portal_my_home" priority="40">
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
<a t-attf-href="/my/act_revise/a">
<button type="button" class="btn btn-primary btn-md o_website_form_send">Печатать акт сверки</button>
</a>
</xpath>
</template>
</odoo>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<template id="report_general_ledger">
<div class="o_act_revise_page">
<t t-call="l10n_ru_act_rev.report_buttons" />
<t t-call="l10n_ru_act_rev.report_general_ledger_base" />
</div>
</template>
</odoo>

View File

@ -0,0 +1,2 @@
from . import abstract_wizard
from . import general_ledger_wizard

View File

@ -0,0 +1,38 @@
from odoo import models, fields
class AbstractWizard(models.AbstractModel):
_name = "act_revise.abstract_wizard"
_description = "Abstract Wizard"
def _get_partner_ids_domain(self):
return [
"&",
"|",
("company_id", "=", self.company_id.id),
("company_id", "=", False),
"|",
("parent_id", "=", False),
("is_company", "=", True),
]
def _default_partners(self):
context = self.env.context
if context.get("active_ids") and context.get("active_model") == "res.partner":
partners = self.env["res.partner"].browse(context["active_ids"])
corp_partners = partners.filtered("parent_id")
partners -= corp_partners
partners |= corp_partners.mapped("commercial_partner_id")
return partners.ids
company_id = fields.Many2one(
comodel_name="res.company",
default=lambda self: self.env.company.id,
required=False,
string="Компания",
)
def button_export_pdf(self):
self.ensure_one()
report_type = "qweb-pdf"
return self._export(report_type)

View File

@ -0,0 +1,897 @@
import logging
import time
from ast import literal_eval
from odoo import _, api, fields, models
from odoo.tools import date_utils,pycompat
from pytils import dt,numeral
from datetime import datetime, date
import re
import urllib
from odoo.exceptions import UserError
class GeneralLedgerReportWizard(models.TransientModel):
"""General ledger report wizard."""
_name = "general.ledger.act_revise.wizard"
_description = "General Ledger Report Wizard"
_inherit = "act_revise.abstract_wizard"
# date_range_id = fields.Many2one(comodel_name="date.range", string="Date range")
date_from = fields.Date(string="Начало даты", required=True, default=lambda self: self._init_date_from())
date_to = fields.Date(string="Конец даты", required=True, default=fields.Date.context_today)
fy_start_date = fields.Date(compute="_compute_fy_start_date")
target_move = fields.Selection(
[("posted", "Все проведенные проводки"), ("all", "Все проводки")],
string="Цель операции",
required=True,
default="posted",
)
account_ids = fields.Many2many(
comodel_name="account.account", string=_("Filter accounts")
)
centralize = fields.Boolean(string=_("Activate centralization"), default=True)
hide_account_at_0 = fields.Boolean(
string=_("Hide account ending balance at 0"),
help=_("Use this filter to hide an account or a partner "
"with an ending balance at 0. "
"If partners are filtered, "
"debits and credits totals will not match the trial balance."),
)
receivable_accounts_only = fields.Boolean()
payable_accounts_only = fields.Boolean()
partner_ids = fields.Many2many(
comodel_name="res.partner",
string=_("Filter partners"),
default=lambda self: self._default_partners(),
)
account_journal_ids = fields.Many2many(
comodel_name="account.journal", string=_("Filter journals")
)
cost_center_ids = fields.Many2many(
comodel_name="account.analytic.account", string=_("Filter cost centers")
)
not_only_one_unaffected_earnings_account = fields.Boolean(readonly=True)
foreign_currency = fields.Boolean(
string=_("Show foreign currency"),
help=_("Display foreign currency for move lines, unless "
"account currency is not setup through chart of accounts "
"will display initial and final balance in that currency."),
default=lambda self: self._default_foreign_currency(),
)
account_code_from = fields.Many2one(
comodel_name="account.account",
help="Starting account in a range",
)
account_code_to = fields.Many2one(
comodel_name="account.account",
help="Ending account in a range",
)
grouped_by = fields.Selection(
selection=[("", "None"), ("partners", "Partners"), ("taxes", "Taxes")],
default="partners",
)
show_cost_center = fields.Boolean(
string="Show Analytic Account",
default=True,
)
domain = fields.Char(
string="Journal Items Domain",
default=[],
help="This domain will be used to select specific domain for Journal " "Items",
)
# def _print_report(self, report_type):
# self.ensure_one()
# data = self._prepare_report_general_ledger()
# report = self.env["ir.actions.report"].search(
# [("report_name", "=", "act_revise.general_ledger"), ("report_type", "=", report_type)], limit=1, )
# if self.partner_ids[0].parent_id:
# partner = int(self.partner_ids[0].parent_id.id)
# else:
# partner = int(self.partner_ids[0].id)
# return {
# 'type': 'ir.actions.act_url',
# 'url': '/my/act_revise_contact/%s?date_to=%s&date_from=%s&target_move=%s&company=%s&partner=%s' % (
# urllib.parse.quote(self.get_report_filename()), self.date_to, self.date_from, self.target_move,
# self.company_id.id, partner),
# 'target': 'new',
# }
def _get_account_move_lines_domain(self):
domain = literal_eval(self.domain) if self.domain else []
return domain
@api.onchange("account_code_from", "account_code_to")
def on_change_account_range(self):
if (
self.account_code_from
and self.account_code_from.code.isdigit()
and self.account_code_to
and self.account_code_to.code.isdigit()
):
start_range = self.account_code_from.code
end_range = self.account_code_to.code
self.account_ids = self.env["account.account"].search(
[("code", ">=", start_range), ("code", "<=", end_range)]
)
if self.company_id:
self.account_ids = self.account_ids.filtered(
lambda a: a.company_ids == self.company_id
)
def _init_date_from(self):
"""set start date to begin of current year if fiscal year running"""
today = fields.Date.context_today(self)
company = self.company_id or self.env.company
last_fsc_month = company.fiscalyear_last_month
last_fsc_day = company.fiscalyear_last_day
if (
today.month < int(last_fsc_month)
or today.month == int(last_fsc_month)
and today.day <= last_fsc_day
):
return time.strftime("%Y-01-01")
else:
return False
def _default_foreign_currency(self):
return self.env.user.has_group("base.group_multi_currency")
@api.depends("date_from")
def _compute_fy_start_date(self):
for wiz in self:
if wiz.date_from:
date_from, date_to = date_utils.get_fiscal_year(
wiz.date_from,
day=self.company_id.fiscalyear_last_day,
month=int(self.company_id.fiscalyear_last_month),
)
wiz.fy_start_date = date_from
else:
wiz.fy_start_date = False
@api.onchange("company_id")
def onchange_company_id(self):
"""Handle company change."""
count = self.env["account.account"].search_count(
[
("account_type", "=", "equity_unaffected"),
("company_ids", "=", self.company_id.id),
]
)
self.not_only_one_unaffected_earnings_account = count != 1
# if (
# self.company_id
# and self.date_range_id.company_id
# and self.date_range_id.company_id != self.company_id
# ):
# self.date_range_id = False
if self.company_id and self.account_journal_ids:
self.account_journal_ids = self.account_journal_ids.filtered(
lambda p: p.company_id == self.company_id or not p.company_id
)
if self.company_id and self.partner_ids:
self.partner_ids = self.partner_ids.filtered(
lambda p: p.company_id == self.company_id or not p.company_id
)
if self.company_id and self.account_ids:
if self.receivable_accounts_only or self.payable_accounts_only:
self.onchange_type_accounts_only()
else:
self.account_ids = self.account_ids.filtered(
lambda a: a.company_ids == self.company_id
)
if self.company_id and self.cost_center_ids:
self.cost_center_ids = self.cost_center_ids.filtered(
lambda c: c.company_id == self.company_id
)
res = {
"domain": {
"account_ids": [],
"partner_ids": [],
"account_journal_ids": [],
"cost_center_ids": [],
# "date_range_id": [],
}
}
if not self.company_id:
return res
else:
res["domain"]["account_ids"] += [("company_ids", "=", self.company_id.id)]
res["domain"]["account_journal_ids"] += [
("company_ids", "=", self.company_id.id)
]
res["domain"]["partner_ids"] += self._get_partner_ids_domain()
res["domain"]["cost_center_ids"] += [
("company_id", "=", self.company_id.id)
]
# res["domain"]["date_range_id"] += [
# "|",
# ("company_id", "=", self.company_id.id),
# ("company_id", "=", False),
# ]
return res
# @api.onchange("date_range_id")
# def onchange_date_range_id(self):
# """Handle date range change."""
# if self.date_range_id:
# self.date_from = self.date_range_id.date_start
# self.date_to = self.date_range_id.date_end
# @api.constrains("company_id", "date_range_id")
# def _check_company_id_date_range_id(self):
# for rec in self.sudo():
# if (
# rec.company_id
# and rec.date_range_id.company_id
# and rec.company_id != rec.date_range_id.company_id
# ):
# raise ValidationError(
# _(
# "The Company in the General Ledger Report Wizard and in "
# "Date Range must be the same."
# )
# )
@api.onchange("receivable_accounts_only", "payable_accounts_only")
def onchange_type_accounts_only(self):
"""Handle receivable/payable accounts only change."""
if self.receivable_accounts_only or self.payable_accounts_only:
domain = [("company_ids", "=", self.company_id.id)]
if self.receivable_accounts_only and self.payable_accounts_only:
domain += [
("account_type", "in", ("asset_receivable", "liability_payable"))
]
elif self.receivable_accounts_only:
domain += [("account_type", "=", "asset_receivable")]
elif self.payable_accounts_only:
domain += [("account_type", "=", "liability_payable")]
self.account_ids = self.env["account.account"].search(domain)
else:
self.account_ids = None
@api.onchange("partner_ids")
def onchange_partner_ids(self):
"""Handle partners change."""
if self.partner_ids:
self.receivable_accounts_only = self.payable_accounts_only = True
else:
self.receivable_accounts_only = self.payable_accounts_only = False
@api.depends("company_id")
def _compute_unaffected_earnings_account(self):
for record in self:
record.unaffected_earnings_account = self.env["account.account"].search(
[
("account_type", "=", "equity_unaffected"),
("company_ids", "=", record.company_id.id),
]
)
unaffected_earnings_account = fields.Many2one(
comodel_name="account.account",
compute="_compute_unaffected_earnings_account",
store=True,
)
# def _print_report(self, report_type):
# self.ensure_one()
# data = self._prepare_report_general_ledger()
# report_name = "act_revise.general_ledger"
# return (
# self.env["ir.actions.report"]
# .search(
# [("report_name", "=", report_name), ("report_type", "=", report_type)],
# limit=1,
# )
# .report_action(self, data=data)
# )
def _print_report(self, report_type):
self.ensure_one()
data = self._prepare_report_general_ledger()
report = self.env["ir.actions.report"].search(
[("report_name", "=", "l10n_ru_act_rev.general_ledger"), ("report_type", "=", report_type)], limit=1, )
if self.partner_ids[0].parent_id:
partner = int(self.partner_ids[0].parent_id.id)
else:
partner = int(self.partner_ids[0].id)
account_data = self.env['account.move.line'].sudo().search([
('partner_id', '=', partner),
('account_id.account_type', 'in', ('liability_payable', 'asset_receivable')),
('account_id.non_trade', '=', False),
('date', '<=', self.date_to),
('date', '>=', self.date_from)
])
if self.target_move == 'posted' and not account_data.filtered(lambda p: p.parent_state == 'posted') or not account_data:
raise UserError(f'Проводок для формирования акта по введенным условиям не найдено.')
return {
'type': 'ir.actions.act_url',
'url': '/my/act_revise_contact/%s?date_to=%s&date_from=%s&target_move=%s&company=%s&partner=%s' % (
urllib.parse.quote(self.get_report_filename()), self.date_to, self.date_from, self.target_move,
self.company_id.id, partner),
'target': 'new',
}
def _prepare_report_general_ledger(self):
self.ensure_one()
return {
"wizard_id": self.id,
"date_from": self.date_from,
"date_to": self.date_to,
"only_posted_moves": self.target_move == "posted",
"hide_account_at_0": self.hide_account_at_0,
"foreign_currency": self.foreign_currency,
"company_id": self.company_id.id,
"account_ids": self.account_ids.ids,
"partner_ids": self.partner_ids.ids,
"grouped_by": self.grouped_by,
"cost_center_ids": self.cost_center_ids.ids,
"show_cost_center": self.show_cost_center,
"journal_ids": self.account_journal_ids.ids,
"centralize": self.centralize,
"fy_start_date": self.fy_start_date,
"unaffected_earnings_account": self.unaffected_earnings_account.id,
"account_financial_report_lang": self.env.lang,
"domain": self._get_account_move_lines_domain(),
}
def _export(self, report_type):
"""Default export is PDF."""
return self._print_report(report_type)
def _get_atr_from_dict(self, obj_id, data, key):
try:
return data[obj_id][key]
except KeyError:
return data[str(obj_id)][key]
def numer(self, name):
if name:
numeration = re.findall('\d+$', name)
if numeration: return numeration[0]
return name
def get_data_format(self, date):
if date and date != 'False':
return dt.ru_strftime(u'%d.%m.%Y г.', date=datetime.strptime(str(date), "%Y-%m-%d"), inflected=True)
return ''
def initials(self, fio):
if fio:
return (fio.split()[0] + ' ' + ''.join([fio[0:1] + '.' for fio in fio.split()[1:]])).strip()
return ''
def rubles(self, sum):
"Transform sum number in rubles to text"
text_rubles = numeral.rubles(int(sum))
copeck = round((sum - int(sum)) * 100)
text_copeck = numeral.choose_plural(int(copeck), (u"копейка", u"копейки", u"копеек"))
return ("%s %02d %s") % (text_rubles, copeck, text_copeck)
def img(self, img, type='png', width=0, height=0):
if width:
width = "width='%spx'" % (width)
else:
width = " "
if height:
height = "height='%spx'" % (height)
else:
height = " "
toreturn = "<img %s %s src='data:image/%s;base64,%s' />" % (
width,
height,
type,
str(pycompat.to_text(img)))
return toreturn
def get_contract(self):
partner = int(self.partner_ids[0].id)
contract = self.env['partner.contract.customer'].search(
[('partner_id', '=', partner), ('state', '=', 'signed')], limit=1)
if contract:
return contract
def get_function_partner(self, partner):
director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
if director:
if director.function:
return director.function or 'отсутствует'
def get_name_partner(self, partner):
director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
if director:
return director.name or 'отсутствует'
def get_report_filename(self):
today = date.today()
d1 = today.strftime("%d-%m-%Y")
if self.partner_ids[0].parent_id:
p = ''.join(self.partner_ids[0].parent_id.name)
else:
p = ''.join(self.partner_ids[0].name)
# return 'Акт Сверки '+ d1 + ' ' + self.company_id.name+'_'+p
return str(self.company_id.id) + ' - ' + ' ' + d1
def sorted_lines(self, list):
list = sorted(list, key=lambda k: k.get('date'), reverse=False)
return list
#
# import time
# from ast import literal_eval
# from odoo import _, api, fields, models
# from odoo.tools import date_utils,pycompat
# from pytils import dt,numeral
# from datetime import datetime, date
# import re
# import urllib
#
# class GeneralLedgerReportWizard(models.TransientModel):
# """General ledger report wizard."""
#
# _name = "general.ledger.act_revise.wizard"
# _description = "General Ledger Report Wizard"
# _inherit = "act_revise.abstract_wizard"
#
# # date_range_id = fields.Many2one(comodel_name="date.range", string="Date range")
# date_from = fields.Date(string="Начало даты", required=True, default=lambda self: self._init_date_from())
# date_to = fields.Date(string="Конец даты", required=True, default=fields.Date.context_today)
# fy_start_date = fields.Date(compute="_compute_fy_start_date")
# target_move = fields.Selection(
# [("posted", "Все проведенные проводки"), ("all", "Все проводки")],
# string="Цель операции",
# required=True,
# default="posted",
# )
# account_ids = fields.Many2many(
# comodel_name="account.account", string=_("Filter accounts")
# )
# centralize = fields.Boolean(string=_("Activate centralization"), default=True)
# hide_account_at_0 = fields.Boolean(
# string=_("Hide account ending balance at 0"),
# help=_("Use this filter to hide an account or a partner "
# "with an ending balance at 0. "
# "If partners are filtered, "
# "debits and credits totals will not match the trial balance."),
# )
# receivable_accounts_only = fields.Boolean()
# payable_accounts_only = fields.Boolean()
# partner_ids = fields.Many2many(
# comodel_name="res.partner",
# string=_("Filter partners"),
# default=lambda self: self._default_partners(),
# )
# account_journal_ids = fields.Many2many(
# comodel_name="account.journal", string=_("Filter journals")
# )
# cost_center_ids = fields.Many2many(
# comodel_name="account.analytic.account", string=_("Filter cost centers")
# )
#
# not_only_one_unaffected_earnings_account = fields.Boolean(readonly=True)
# foreign_currency = fields.Boolean(
# string=_("Show foreign currency"),
# help=_("Display foreign currency for move lines, unless "
# "account currency is not setup through chart of accounts "
# "will display initial and final balance in that currency."),
# default=lambda self: self._default_foreign_currency(),
# )
# account_code_from = fields.Many2one(
# comodel_name="account.account",
# help="Starting account in a range",
# )
# account_code_to = fields.Many2one(
# comodel_name="account.account",
# help="Ending account in a range",
# )
# grouped_by = fields.Selection(
# selection=[("", "None"), ("partners", "Partners"), ("taxes", "Taxes")],
# default="partners",
# )
# show_cost_center = fields.Boolean(
# string="Show Analytic Account",
# default=True,
# )
# domain = fields.Char(
# string="Journal Items Domain",
# default=[],
# help="This domain will be used to select specific domain for Journal " "Items",
# )
#
# # def _print_report(self, report_type):
# # self.ensure_one()
# # data = self._prepare_report_general_ledger()
# # report = self.env["ir.actions.report"].search(
# # [("report_name", "=", "act_revise.general_ledger"), ("report_type", "=", report_type)], limit=1, )
# # if self.partner_ids[0].parent_id:
# # partner = int(self.partner_ids[0].parent_id.id)
# # else:
# # partner = int(self.partner_ids[0].id)
# # return {
# # 'type': 'ir.actions.act_url',
# # 'url': '/my/act_revise_contact/%s?date_to=%s&date_from=%s&target_move=%s&company=%s&partner=%s' % (
# # urllib.parse.quote(self.get_report_filename()), self.date_to, self.date_from, self.target_move,
# # self.company_id.id, partner),
# # 'target': 'new',
# # }
#
# def _get_account_move_lines_domain(self):
# domain = literal_eval(self.domain) if self.domain else []
# return domain
#
# @api.onchange("account_code_from", "account_code_to")
# def on_change_account_range(self):
# if (
# self.account_code_from
# and self.account_code_from.code.isdigit()
# and self.account_code_to
# and self.account_code_to.code.isdigit()
# ):
# start_range = self.account_code_from.code
# end_range = self.account_code_to.code
# self.account_ids = self.env["account.account"].search(
# [("code", ">=", start_range), ("code", "<=", end_range)]
# )
# if self.company_id:
# self.account_ids = self.account_ids.filtered(
# lambda a: a.company_id == self.company_id
# )
#
# def _init_date_from(self):
# """set start date to begin of current year if fiscal year running"""
# today = fields.Date.context_today(self)
# company = self.company_id or self.env.company
# last_fsc_month = company.fiscalyear_last_month
# last_fsc_day = company.fiscalyear_last_day
#
# if (
# today.month < int(last_fsc_month)
# or today.month == int(last_fsc_month)
# and today.day <= last_fsc_day
# ):
# return time.strftime("%Y-01-01")
# else:
# return False
#
# def _default_foreign_currency(self):
# return self.env.user.has_group("base.group_multi_currency")
#
# @api.depends("date_from")
# def _compute_fy_start_date(self):
# for wiz in self:
# if wiz.date_from:
# date_from, date_to = date_utils.get_fiscal_year(
# wiz.date_from,
# day=self.company_id.fiscalyear_last_day,
# month=int(self.company_id.fiscalyear_last_month),
# )
# wiz.fy_start_date = date_from
# else:
# wiz.fy_start_date = False
#
# @api.onchange("company_id")
# def onchange_company_id(self):
# """Handle company change."""
# count = self.env["account.account"].search_count(
# [
# ("account_type", "=", "equity_unaffected"),
# ("company_id", "=", self.company_id.id),
# ]
# )
# self.not_only_one_unaffected_earnings_account = count != 1
# # if (
# # self.company_id
# # and self.date_range_id.company_id
# # and self.date_range_id.company_id != self.company_id
# # ):
# # self.date_range_id = False
# if self.company_id and self.account_journal_ids:
# self.account_journal_ids = self.account_journal_ids.filtered(
# lambda p: p.company_id == self.company_id or not p.company_id
# )
# if self.company_id and self.partner_ids:
# self.partner_ids = self.partner_ids.filtered(
# lambda p: p.company_id == self.company_id or not p.company_id
# )
# if self.company_id and self.account_ids:
# if self.receivable_accounts_only or self.payable_accounts_only:
# self.onchange_type_accounts_only()
# else:
# self.account_ids = self.account_ids.filtered(
# lambda a: a.company_id == self.company_id
# )
# if self.company_id and self.cost_center_ids:
# self.cost_center_ids = self.cost_center_ids.filtered(
# lambda c: c.company_id == self.company_id
# )
# res = {
# "domain": {
# "account_ids": [],
# "partner_ids": [],
# "account_journal_ids": [],
# "cost_center_ids": [],
# # "date_range_id": [],
# }
# }
# if not self.company_id:
# return res
# else:
# res["domain"]["account_ids"] += [("company_id", "=", self.company_id.id)]
# res["domain"]["account_journal_ids"] += [
# ("company_id", "=", self.company_id.id)
# ]
# res["domain"]["partner_ids"] += self._get_partner_ids_domain()
# res["domain"]["cost_center_ids"] += [
# ("company_id", "=", self.company_id.id)
# ]
# # res["domain"]["date_range_id"] += [
# # "|",
# # ("company_id", "=", self.company_id.id),
# # ("company_id", "=", False),
# # ]
# return res
#
# # @api.onchange("date_range_id")
# # def onchange_date_range_id(self):
# # """Handle date range change."""
# # if self.date_range_id:
# # self.date_from = self.date_range_id.date_start
# # self.date_to = self.date_range_id.date_end
#
# # @api.constrains("company_id", "date_range_id")
# # def _check_company_id_date_range_id(self):
# # for rec in self.sudo():
# # if (
# # rec.company_id
# # and rec.date_range_id.company_id
# # and rec.company_id != rec.date_range_id.company_id
# # ):
# # raise ValidationError(
# # _(
# # "The Company in the General Ledger Report Wizard and in "
# # "Date Range must be the same."
# # )
# # )
#
# @api.onchange("receivable_accounts_only", "payable_accounts_only")
# def onchange_type_accounts_only(self):
# """Handle receivable/payable accounts only change."""
# if self.receivable_accounts_only or self.payable_accounts_only:
# domain = [("company_id", "=", self.company_id.id)]
# if self.receivable_accounts_only and self.payable_accounts_only:
# domain += [
# ("account_type", "in", ("asset_receivable", "liability_payable"))
# ]
# elif self.receivable_accounts_only:
# domain += [("account_type", "=", "asset_receivable")]
# elif self.payable_accounts_only:
# domain += [("account_type", "=", "liability_payable")]
# self.account_ids = self.env["account.account"].search(domain)
# else:
# self.account_ids = None
#
# @api.onchange("partner_ids")
# def onchange_partner_ids(self):
# """Handle partners change."""
# if self.partner_ids:
# self.receivable_accounts_only = self.payable_accounts_only = True
# else:
# self.receivable_accounts_only = self.payable_accounts_only = False
#
# @api.depends("company_id")
# def _compute_unaffected_earnings_account(self):
# for record in self:
# record.unaffected_earnings_account = self.env["account.account"].search(
# [
# ("account_type", "=", "equity_unaffected"),
# ("company_id", "=", record.company_id.id),
# ]
# )
#
# unaffected_earnings_account = fields.Many2one(
# comodel_name="account.account",
# compute="_compute_unaffected_earnings_account",
# store=True,
# )
#
# # def _print_report(self, report_type):
# # self.ensure_one()
# # data = self._prepare_report_general_ledger()
# # report_name = "act_revise.general_ledger"
# # return (
# # self.env["ir.actions.report"]
# # .search(
# # [("report_name", "=", report_name), ("report_type", "=", report_type)],
# # limit=1,
# # )
# # .report_action(self, data=data)
# # )
# def _print_report(self, report_type):
# self.ensure_one()
# data = self._prepare_report_general_ledger()
# report = self.env["ir.actions.report"].search(
# [("report_name", "=", "act_revise.general_ledger"), ("report_type", "=", report_type)], limit=1, )
# # report.report_name='Test'
# # report.headers.add('Content-Disposition', 'attachment; filename="Test.pdf";')
# # pdf, _ = request.env.ref('act_revise.action_print_report_general_ledger_qweb').sudo().render_qweb_pdf(self,data=data)
# # pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf)),('Content-Disposition', 'attachment; filename="%s";' % report_name),]
# # return request.make_response(pdf, headers=pdfhttpheaders)
# # return (
# # self.env["ir.actions.report"]
# # .search(
# # [("report_name", "=", "act_revise.general_ledger"), ("report_type", "=", report_type)],
# # limit=1,
# # )
# # report.report_action(self, data=data)
# # )
# # name='Test'
# if self.partner_ids[0].parent_id:
# partner = int(self.partner_ids[0].parent_id.id)
# else:
# partner = int(self.partner_ids[0].id)
# account_data = self.env['account.move.line'].sudo().search([
# ('partner_id', '=', partner.id),
# ('account_id.account_type', 'in', ('liability_payable', 'asset_receivable')),
# ('account_id.non_trade', '=', False),
# ('date', '<=', self.date_to),
# ('date', '>=', self.date_from)
# ])
# if not account_data:
# raise UserError(f'Проводок для формирования акта по введенным условиям не найдено.')
# return {
# 'type': 'ir.actions.act_url',
# 'url': '/my/act_revise_contact/%s?date_to=%s&date_from=%s&target_move=%s&company=%s&partner=%s' % (
# urllib.parse.quote(self.get_report_filename()), self.date_to, self.date_from, self.target_move,
# self.company_id.id, partner),
# 'target': 'new',
# }
#
# def _prepare_report_general_ledger(self):
# self.ensure_one()
# return {
# "wizard_id": self.id,
# "date_from": self.date_from,
# "date_to": self.date_to,
# "only_posted_moves": self.target_move == "posted",
# "hide_account_at_0": self.hide_account_at_0,
# "foreign_currency": self.foreign_currency,
# "company_id": self.company_id.id,
# "account_ids": self.account_ids.ids,
# "partner_ids": self.partner_ids.ids,
# "grouped_by": self.grouped_by,
# "cost_center_ids": self.cost_center_ids.ids,
# "show_cost_center": self.show_cost_center,
# "journal_ids": self.account_journal_ids.ids,
# "centralize": self.centralize,
# "fy_start_date": self.fy_start_date,
# "unaffected_earnings_account": self.unaffected_earnings_account.id,
# "account_financial_report_lang": self.env.lang,
# "domain": self._get_account_move_lines_domain(),
# }
#
# def _export(self, report_type):
# """Default export is PDF."""
# return self._print_report(report_type)
#
# def _get_atr_from_dict(self, obj_id, data, key):
# try:
# return data[obj_id][key]
# except KeyError:
# return data[str(obj_id)][key]
#
# def numer(self, name):
# if name:
# numeration = re.findall('\d+$', name)
# if numeration: return numeration[0]
# return name
#
# def get_data_format(self, date):
# if date and date != 'False':
# return dt.ru_strftime(u'%d.%m.%Y Рі.', date=datetime.strptime(str(date), "%Y-%m-%d"), inflected=True)
# return ''
#
# def initials(self, fio):
# if fio:
# return (fio.split()[0] + ' ' + ''.join([fio[0:1] + '.' for fio in fio.split()[1:]])).strip()
# return ''
#
# def rubles(self, sum):
# "Transform sum number in rubles to text"
# text_rubles = numeral.rubles(int(sum))
# copeck = round((sum - int(sum)) * 100)
# text_copeck = numeral.choose_plural(int(copeck), (u"копейка", u"копейки", u"копеек"))
# return ("%s %02d %s") % (text_rubles, copeck, text_copeck)
#
# def img(self, img, type='png', width=0, height=0):
# if width:
# width = "width='%spx'" % (width)
# else:
# width = " "
# if height:
# height = "height='%spx'" % (height)
# else:
# height = " "
# toreturn = "<img %s %s src='data:image/%s;base64,%s' />" % (
# width,
# height,
# type,
# str(pycompat.to_text(img)))
# return toreturn
#
# def get_contract(self):
# partner = int(self.partner_ids[0].id)
# contract = self.env['partner.contract.customer'].search(
# [('partner_id', '=', partner), ('state', '=', 'signed')], limit=1)
# if contract:
# return contract
#
# def get_function_partner(self, partner):
# director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
# if director:
# if director.function:
# return director.function or 'отсутствует'
#
# def get_name_partner(self, partner):
# director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
# if director:
# return director.name or 'отсутствует'
#
# def get_report_filename(self):
# today = date.today()
# d1 = today.strftime("%d-%m-%Y")
# if self.partner_ids[0].parent_id:
# p = ''.join(self.partner_ids[0].parent_id.name)
# else:
# p = ''.join(self.partner_ids[0].name)
# # return 'Акт Сверки '+ d1 + ' ' + self.company_id.name+'_'+p
# return str(self.company_id.id) + ' - ' + ' ' + d1
#
# def sorted_lines(self, list):
# list = sorted(list, key=lambda k: k.get('date'), reverse=False)
# return list

View File

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- GENERAL LEDGER -->
<record id="general_ledger_wizard" model="ir.ui.view">
<field name="name">Акт сверки</field>
<field name="model">general.ledger.act_revise.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main_info">
<field
name="company_id"
options="{'no_create': True}"
groups="base.group_multi_company"
/>
</group>
<div invisible= "not_only_one_unaffected_earnings_account==True" >
<group name="filters">
<group name="date_range">
<!-- <field name="date_range_id" />-->
<field name="date_from" />
<field name="date_to" />
<field name="fy_start_date" invisible="1" /><!-- reason: need-->
<field name="target_move" widget="radio" />
</group>
<group name="other_filters" invisible="1"><!-- reason: need-->
<field name="grouped_by" />
<field name="centralize" />
<field name="hide_account_at_0" />
<field name="foreign_currency" />
<field name="show_cost_center" />
</group>
</group>
<notebook invisible="1"><!-- reason: need-->
<page string="Filter accounts">
<group name="account_filter" col="4">
<label for="account_ids" colspan="4" />
<field name="receivable_accounts_only" />
<field name="payable_accounts_only" />
<label for="account_code_from" string="From Code" />
<div>
<div class="o_row">
<field
name="account_code_from"
class="oe_inline"
options="{'no_create': True}"
/>
<span class="oe_inline">To</span>
<field
name="account_code_to"
class="oe_inline"
options="{'no_create': True}"
/>
</div>
</div>
<field
name="account_ids"
nolabel="1"
widget="many2many_tags"
options="{'no_create': True}"
colspan="4"
/>
</group>
</page>
<page string="Filter partners">
<field
name="partner_ids"
nolabel="1"
widget="many2many_tags"
options="{'no_create': True}"
/>
</page>
<page
string="Filter analytic accounts"
groups="analytic.group_analytic_accounting"
>
<field
name="cost_center_ids"
nolabel="1"
widget="many2many_tags"
options="{'no_create': True}"
/>
</page>
<page string="Additional Filtering">
<style>
.o_domain_show_selection_button {display: none}
</style>
<field
name="domain"
widget="domain"
options="{'model': 'account.move.line', 'in_dialog': True}"
context="{'skip_search_count': 1}"
/>
</page>
</notebook>
</div>
<div invisible= "not_only_one_unaffected_earnings_account==False" >
<field name="not_only_one_unaffected_earnings_account" invisible="1"/><!-- reason: need-->
<group />
<h4>
General Ledger can be computed only if selected company have
only one unaffected earnings account.
</h4>
<group />
</div>
<footer>
<div invisible= "not_only_one_unaffected_earnings_account==True">
<button
name="button_export_pdf"
string="Печать"
type="object"
/>
<button string="Отмена" class="oe_link" special="cancel" />
</div>
<div invisible= "not_only_one_unaffected_earnings_account==False" >
<button string="Отмена" class="oe_link" special="cancel" />
</div>
</footer>
</form>
</field>
</record>
<!-- <act_window-->
<!-- id="action_general_ledger_wizard"-->
<!-- name="Акт сверки"-->
<!-- res_model="general.ledger.act_revise.wizard"-->
<!-- view_mode="form"-->
<!-- view_id="general_ledger_wizard"-->
<!-- target="new"-->
<!-- />-->
<!-- &lt;!&ndash;Add to res.partner action&ndash;&gt;-->
<!-- <act_window-->
<!-- id="act_action_general_ledger_wizard_partner_relation"-->
<!-- name="Печатать акт сверки"-->
<!-- res_model="general.ledger.act_revise.wizard"-->
<!-- binding_model="res.partner"-->
<!-- view_mode="form"-->
<!-- context="{-->
<!-- 'default_receivable_accounts_only':1,-->
<!-- 'default_payable_accounts_only':1,-->
<!-- }"-->
<!-- groups="account.group_account_manager"-->
<!-- target="new"-->
<!-- />-->
<record id="action_general_ledger_wizard" model="ir.actions.act_window">
<field name="name">Акт сверки</field>
<field name="res_model">general.ledger.act_revise.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="general_ledger_wizard"/>
<field name="target">new</field>
</record>
<record id="act_action_general_ledger_wizard_partner_relation" model="ir.actions.act_window">
<field name="name">Печатать акт сверки</field>
<field name="res_model">general.ledger.act_revise.wizard</field>
<field name="binding_model_id" ref="base.model_res_partner"/>
<field name="view_mode">form</field>
<field name="context">{'default_receivable_accounts_only': 1, 'default_payable_accounts_only': 1}</field>
<field name="groups_id" eval="[(6, 0, [ref('account.group_account_manager')])]"/>
<field name="target">new</field>
</record>
</odoo>

View File

@ -0,0 +1,21 @@
# Российская локализация - Доверенность
name: l10n_ru_attorney
## Описание
Создание списка доверенностей на получение ТМЦ и их печать.
###Создание доверенности:
1. Меню Покупки - Доверенности - кнопка "Создать";
2. На форме указываем:
2.1. Контрагент - поставщик;
2.2. Заказ на закупку;
2.3. Даты действия доверенности ("дата выдачи" и "действительно по").
###Для печати:
1. Меню Настройки - Техническое - Отчеты;
2. Находим в списке l10n_ru_attorney и добавляем в меню "Печать";
3. Открываем созданную запись доверенности - Действие - "Доверенность".

View File

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

View File

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
{
'name': "Российская локализация - Доверенность",
'summary': """
Печать доверенности на получение ТМЦ
""",
'description': """
Создание списка доверенностей на получение ТМЦ и их печать.
Создание доверенности:
1. Меню Покупки - Доверенности - кнопка "Создать";
2. На форме указываем:
2.1. Контрагент - поставщик;
2.2. Заказ на закупку;
2.3. Даты действия доверенности ("дата выдачи" и "действительно по").
Для печати:
1. Меню Настройки - Техническое - Отчеты;
2. Находим в списке l10n_ru_attorney и добавляем в меню "Печать";
3. Открываем созданную запись доверенности - Действие - "Доверенность".
""",
'author': "MK.Lab",
'website': "https://www.inf-centre.ru/",
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base', 'account', 'sale', 'purchase', 'hr', 'l10n_ru_base'],
# always loaded
'data': [
'security/ir.model.access.csv',
'views/base_consent_views.xml',
'views/hr_employee_views.xml',
'views/purchase_order_views.xml',
'report/consent_report.xml',
],
}

View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import base_consent
from . import hr_employee
from . import purchase_order

Binary file not shown.

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from datetime import datetime, timedelta
class BaseConsent(models.Model):
_name = 'base.consent'
_inherit = ['mail.thread', 'utm.mixin']
_description = 'Consent'
_order = 'date_from desc'
name = fields.Char(string=_('Номер'))
date_from = fields.Date(string=_('Дата выдачи'), default=lambda self: fields.Datetime.now())
date_to = fields.Date(string=_('Действительна по'), default=lambda self: datetime.today() + timedelta(days=180))
partner_id = fields.Many2one('res.partner', string=_('Контрагент'), required=1)
employee_id = fields.Many2one('hr.employee', string=_('Сотрудник'), required=1)
purchaseorder_id = fields.Many2one('purchase.order', _('Заказ на закупку'), domain="[('partner_id','=',partner_id)]",
required=1)
company_id = fields.Many2one('res.company', string=_('Компания'),
default=lambda self: self.env['res.company']._company_default_get('base.consent'),
required=1)
@api.model
def create(self, val):
name = self.env['ir.sequence'].next_by_code('base.consent')
if name:
if 'name' in val:
if val['name'] == False:
val.update({
'name': name,
})
result = super(BaseConsent, self).create(val)
return result
@api.onchange('purchaseorder_id')
def set_partner(self):
if self.purchaseorder_id:
self.partner_id = self.purchaseorder_id.partner_id
@api.constrains('purchaseorder_id')
def fill_order(self):
p_orders = self.env['purchase.order'].sudo().browse(self.purchaseorder_id.id)
for order in p_orders:
order.consent_id = self.id

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from odoo import fields, models, _
class HrEmployee(models.Model):
_inherit = 'hr.employee'
inn = fields.Char(string=_("ИНН"))
pass_kem = fields.Char(string=_("Кем выдан паспорт"))
pass_date = fields.Date(string=_('Дата выдачи паспорта'))
class HrEmployeePublic(models.Model):
_inherit = 'hr.employee.public'
inn = fields.Char(related='employee_id.inn', readonly = True)
pass_kem = fields.Char(related='employee_id.pass_kem', readonly = True)
pass_date = fields.Date(related='employee_id.pass_date', readonly = True)

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
from odoo import fields, models, _
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
consent_id = fields.Many2one('base.consent', string=_('Доверенность'))

View File

@ -0,0 +1,303 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<report
string="Доверенность"
id="action_report_consent"
model="base.consent"
report_type="qweb-pdf"
name="l10n_ru_attorney.report_consent"
file="l10n_ru_attorney.report_consent"
/>
<record model="ir.actions.report" id="l10n_ru_attorney.action_report_consent">
<field name="name">Доверенность</field>
<field name="model">base.consent</field>
<field name="print_report_name">(u'Доверенность - %s.pdf' % (object.name))</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_ru_attorney.report_consent</field>
</record>
<record id="paperformat_a4new" model="report.paperformat">
<field name="name">A4</field>
<field name="default" eval="True"/>
<field name="format">A4</field>
<field name="page_height">0</field>
<field name="page_width">0</field>
<field name="orientation">Portrait</field>
<field name="margin_top">15</field>
<field name="margin_bottom">15</field>
<field name="margin_left">7</field>
<field name="margin_right">7</field>
<field name="header_line" eval="False"/>
<field name="header_spacing">10</field>
<field name="dpi">90</field>
</record>
<template id="l10n_ru_attorney.report_consent">
<t t-call="web.basic_layout">
<t t-foreach="docs" t-as="o">
<div class="page">
<style type="text/css">
/*----------------Invoice classes-------------------*/
body { background: #ffffff; font-family: Arial; font-size: 8pt; font-style: normal; }
p { font-family: Arial; font-size: 13px; }
.tb-numbers { width: 600px; }
.tb-numbers td { padding: 4px 10px; font-family: Arial; }
.tb-numbers label {display: inline-block;padding: 4px 0px 0px;line-height: 20px;font-size:
12px;font-family: Arial;}
.tb-numbers input {width: 100px;}
.company-info {width: 100%;margin: 20px auto;}
.company-info td {padding: 5px 2px;font-size: 13px;font-family: Arial;}
.approver-info {width: 100%;margin: 20px auto;text-align: center;}
.approver-info .director {font-weight: bold;}
.approver-info .title {font-weight: bold;font-size: 18px;padding-bottom: 20px;}
.approver-info .signature {font-size: 11px;padding-top: 40px;}
.actDate {float: right;}
.actDescription {width: 100%;padding-top: 30px;}
.act-info p {font-weight: bold;}
.buyer-box {width: 50%;}
.buyer-box table {width: 100%;}
.buyer-box .tb-info {width: 310px;}
.buyer-box .tb-info td {padding: 2px 5px;}
.buyer-box .tb-info td.lbl {text-align: right;font-weight: bold;width: 40%;}
.tb-invoice {width: 100%;border-collapse: collapse;}
.tb-invoice td {padding: 2px 4px;border: 1px solid #ccc;font-size: 11px;text-align:
center;font-family: Arial;}
.tb-invoice td input {border: 0px;text-align: center;}
.tb-invoice .head td {background: #f3f3f3;font-weight: bold;}
.deleteRow {color: Red;font-size: 13px;font-weight: bold;cursor: pointer;}
.tb-act {width: 100%;border-collapse: collapse;margin-bottom: 15px;clear: both;margin: 20px
0px 10px 0px;}
.tb-act td {padding: 2px 4px;border: 1px solid #ccc;font-size:
13px;text-align:center;font-family: Arial;}
.tb-act td input {border: 0px;text-align:center;}
.tb-act .head td {background:#f3f3f3;}
.tb-act td .deleteActRow {color: Red;font-size :13 px;font-weight:bold;cursor:pointer;}
.description {font-size :12 px;text-align :justify;text-indent :1.5 em;white-space
:pre-wrap;}
.tb-total {width :405 px;margin: 30px 0px 20px auto;}
.tb-total td {padding: 2px 5px;font-size :12 px;}
.tb-total td.lbl {font-weight:bold;text-align:right;}
.tb-total td.val {border :1 px solid #ccc ;min-width :50 px ;}
.tb-total td input {padding :1 px ;font-size :11 px ;}
.act-link-addrow-container {margin-bottom :30 px ;}
.link-addrow, .act-link-addrow {text-decoration:none !important ;border-bottom :1 px dashed
#777 ;font-size :13 px ;}
.btn-box {margin: 10px 0px;}
.signer-box {width: 400px;}
.buyer-box .tb-info {width: 400px;}
.btn-box a {font-size: 14px;margin-left: 10px;}
.podpis-box {clear: both;display: inline-block;font-size: 11px;margin: 20px auto 30px;width:
100%;}
.tb-podpis {width: 100%;}
.podpis-box .box1 {float: left;width: 48%;}
/*SIGNATURES PLACE BEGIN*/
.signatures-box {clear: both;display: inline-block;font-size: 11px;margin: 20px auto
30px;width: 100%;}
.signatures-box .box1 {float: left;width: 35%;}
.signatures-box .box2 {float: right;width: 35%;}
.signatures-box .box1 table,
.signatures-box .box2 table { width: 100%; }
.signatures-box table td { padding: 3px; text-align: center; }
/*SIGNATURES PLACE END*/
.podpis-box .box2 { float: right; width: 48%; }
.podpis-box .box1 table,
.podpis-box .box2 table { width: 100%; }
.podpis-box table td { padding: 3px; text-align: center; }
.special-line { border-bottom: 1px solid #000; display: block; line-height: 22px
!important;text-align: center; }
.w120 { width: 120px; }
.w130 { width: 130px; }
.note { font-size: 11px !important; }
.l { text-align: left !important; }
.r { text-align: right !important; }
.c { text-align: center !important; }
</style>
<div>
<h4 style="text-align:center; margin:10px 5px 5px;">
Доверенность №
<span t-field="o.name"/>
</h4>
</div>
<table style="width:55%">
<tr>
<td>Дата выдачи:</td>
<td>
<span t-field="o.date_from" t-options="{&quot;widget&quot;: &quot;date&quot;}"/>
г.
</td>
</tr>
<tr>
<td>Доверенность действительна по:</td>
<td>
<span t-field="o.date_from" t-options="{&quot;widget&quot;: &quot;date&quot;}"/>
г.
</td>
</tr>
</table>
<div class="special-line" style="width:100%;margin-top:15px;text-align:center">
<b>
<span t-field="o.company_id.name"/>, адрес:<span t-field="o.company_id.street"/>, ИНН
<span t-field="o.company_id.vat"/>,
</b>
</div>
<div style="margin:0 auto;text-align:center;">наименование предприятия и его адрес</div>
<table style="width:100%;margin-top:10px">
<tr>
<td style="width:30%">
Доверенность выдана
</td>
<td style="width:100%;text-align:center;">
<span style="border-bottom:1px solid;display:block;clear:both;text-align:center;">
<span t-field="o.employee_id"/>,<span t-field="o.employee_id.job_id.name"/>, ИНН
<span t-field="o.employee_id.inn"/>
</span>
<span style="">
должность и Ф.И.О.
</span>
</td>
</tr>
</table>
<table style="width:100%">
<tr>
<td>Паспорт серия и №
<span style="padding:0 5px 0 5px;margin-left:12px;margin-right:12px;display:inline-table;"
class="special-line">
<span t-field="o.employee_id.passport_id"/>
</span>
</td>
</tr>
<tr>
<td>Кем выдан
<span style="padding:0 5px 0 5px;margin-left:12px;margin-right:12px;display:inline-table;"
class="special-line">
<span t-field="o.employee_id.pass_kem"/>
</span>
</td>
</tr>
<tr>
<td>Дата выдачи
<span style="padding:0 5px 0 5px;margin-left:12px;margin-right:12px;display:inline-table;"
class="special-line">
<span t-field="o.employee_id.pass_date"/>
</span>
</td>
</tr>
</table>
<table style="width:100%">
<tr>
<td style="width:25%">
На получение от
</td>
<td style="width:75%;text-align:center;">
<span style="border-bottom:1px solid;display:block;clear:both;">
<span t-field="o.partner_id"/>
</span>
<span style="">
наименование поставщика
</span>
</td>
</tr>
<tr>
<td style="width:25%">
Материальных ценностей по
</td>
<td style="width:75%;text-align:center;">
<span style="border-bottom:1px solid;display:block;clear:both;">
Заказу на закупку №
<span t-field="o.purchaseorder_id.name"/>
от
<span t-field="o.purchaseorder_id.date_order"
t-options="{&quot;widget&quot;: &quot;date&quot;}"/>
г.
</span>
<span style="">
наименование, номер и дата документа
</span>
</td>
</tr>
</table>
<div>
<h4 style="text-align:center; margin:15px 5px 15px;">
Перечень материальных ценностей,
<br/>
подлежащих получению
</h4>
</div>
<table class="tb-invoice">
<tr class="head">
<td></td>
<td style="min-width:200px;">Наименование товаров (работ, услуг)</td>
<td>Ед. изм.</td>
<td>Количество</td>
</tr>
<t t-set="num" t-value="1"/>
<t t-foreach="o.purchaseorder_id.order_line" t-as="line">
<tr class="data-row">
<td>
<t t-esc="num"/>
</td>
<td class="l" style="white-space: pre-wrap;">
<span t-field="line.name"/>
</td>
<td>
<span t-field="line.product_uom"/>
</td>
<td>
<span t-field="line.product_qty"/>
</td>
</tr>
<t t-set="num" t-value="num+1"/>
</t>
</table>
<div class="clear h100"></div>
<div>
<p></p>
<p>Подпись лица, получившего доверенность ___________________ удостоверяем</p>
</div>
<div class="podpis-box">
<table style="width:50%;">
<tr>
<td style="width: 13%;">Руководитель предприятия</td>
<td style="width: 19%; vertical-align: bottom;">
<span class="special-line"></span>
<span class="note">(подпись)</span>
</td>
<td style="width: 16%; vertical-align: bottom;">
<span class="special-line"></span>
<span class="note">(Ф.И.О)</span>
</td>
</tr>
<tr>
<td style="width: 13%;">Гл. бухгалтер:</td>
<td style="width: 19%; vertical-align: bottom;">
<span class="special-line"></span>
<span class="note">(подпись)</span>
</td>
<td style="width: 16%; vertical-align: bottom;">
<span class="special-line"></span>
<span class="note">(Ф.И.О)</span>
</td>
</tr>
</table>
</div>
</div>
</t>
</t>
</template>
</data>
</odoo>

View File

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_base_consent,base.consent,model_base_consent,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_base_consent base.consent model_base_consent base.group_user 1 1 1 1

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="base_consent_tree" model="ir.ui.view">
<field name="name">Consents</field>
<field name="model">base.consent</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
<field name="partner_id"/>
<field name="employee_id"/>
<field name="date_from"/>
<field name="date_to"/>
</list>
</field>
</record>
<record id="base_consent_form" model="ir.ui.view">
<field name="name">consent.form</field>
<field name="model">base.consent</field>
<field name="arch" type="xml">
<form>
<header></header>
<sheet>
<group>
<group>
<field name="name" placeholder="Для автонумерации оставьте пустым"/>
<field name="partner_id"/>
<field name="employee_id"/>
</group>
<group>
<field name="date_from"/>
<field name="date_to"/>
<field name="purchaseorder_id"/>
<field name="company_id"/>
</group>
</group>
</sheet>
<chatter/>
<!-- <div class="oe_chatter">-->
<!-- <field name="message_follower_ids" widget="mail_followers"/>-->
<!-- <field name="message_ids" widget="mail_thread"/>-->
<!-- </div>-->
</form>
</field>
</record>
<record id="l10n_ru_attorney.action_consent" model="ir.actions.act_window">
<field name="name">Доверенности</field>
<field name="res_model">base.consent</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="l10n_ru_attorney.menu_1" name="Доверенности" parent="purchase.menu_purchase_root"
action="l10n_ru_attorney.action_consent"/>
<record id="seq_consent" model="ir.sequence">
<field name="name">Consents</field>
<field name="code">base.consent</field>
<field name="prefix">CON</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_employee_form" model="ir.ui.view">
<field name="name">view_employee_form.inherit</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_account_id']" position="after">
<field name="inn"/>
</xpath>
<xpath expr="//field[@name='passport_id']" position="after">
<field name="pass_kem"/>
<field name="pass_date"/>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="purchase_order_form" model="ir.ui.view">
<field name="name">purchase.order.form.inherit</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='incoterm_id']" position="after">
<field name="consent_id" context="{'default_partner_id':partner_id,'default_purchaseorder_id':id}"
domain="[('purchaseorder_id','=',id)]"/>
</xpath>
</field>
</record>
</data>
</odoo>

16
l10n_ru_base/README.md Normal file
View File

@ -0,0 +1,16 @@
# Российская локализация - Базовый
name: l10n_ru_base
## Описание
Российская локализация: основные отчеты и печатные формы. Это базовый модуль для работы с модулями локализации.
###Для включения модулей:
1. Меню Настройки - в боковом меню "Российская локализация";
2. Выбирается нужный блок для подключения дополнительных возможностей по локализации.
###Перечень модулей локализации:
1. Российская локализация - Акт сверки (l10n_ru_act_rev)
2. Российская локализация - Доверенность (l10n_ru_attorney)
3. Российская локализация - Договоры (l10n_ru_contract)
4. Российская локализация - Документы (l10n_ru_doc)
5. Российская локализация - УПД в xml-формате (l10n_ru_upd_xml)

2
l10n_ru_base/__init__.py Normal file
View File

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

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
{
'name': "Российская локализация - Базовый>",
'summary': """
Российская локализация: основные отчеты и печатные формы.
""",
'description': """
Российская локализация: основные отчеты и печатные формы.
Для включения модулей:
1. Меню Настройки - в боковом меню "Российская локализация"
2. Выбирается нужный блок.
""",
'author': "MK.Lab",
'website': "https://www.inf-centre.ru/",
'version': '18.0.1.0.0',
'license': 'LGPL-3',
'category': 'Uncategorized',
'depends': ["account", "portal", "website", 'contacts','base', 'mail', 'sale', 'purchase', 'hr','sale_management', 'sale_stock', 'uom','web'],
'data': [
'views/res_config_settings_views.xml',
],
}

117
l10n_ru_base/i18n/ru_RU.po Normal file
View File

@ -0,0 +1,117 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_ru_base
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-12-17 06:36+0000\n"
"PO-Revision-Date: 2024-12-17 06:36+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: l10n_ru_base
#. odoo-python
#: code:addons/l10n_ru_base/models/res_config_settings.py:0
#: model:ir.model.fields,field_description:l10n_ru_base.field_res_config_settings__module_l10n_ru_act_rev
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "Act revise"
msgstr "Акт сверки"
#. module: l10n_ru_base
#. odoo-python
#: code:addons/l10n_ru_base/models/res_config_settings.py:0
#: model:ir.model.fields,field_description:l10n_ru_base.field_res_config_settings__module_act_revise
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
#, python-format
msgid "Act revise"
msgstr "Акт сверки"
#. module: l10n_ru_base
#: model:ir.model.fields,field_description:l10n_ru_base.field_res_config_settings__company_status_rf
msgid "Company Status Rf"
msgstr "Статус компании"
#. module: l10n_ru_base
#: model:ir.model,name:l10n_ru_base.model_res_config_settings
msgid "Config Settings"
msgstr "Параметры конфигурации"
#. module: l10n_ru_base
#. odoo-python
#: code:addons/l10n_ru_base/models/res_config_settings.py:0
#: model:ir.model.fields,field_description:l10n_ru_base.field_res_config_settings__module_fehu_base_consent
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
#, python-format
msgid "Consent"
msgstr "Доверенность"
#. module: l10n_ru_base
#. odoo-python
#: code:addons/l10n_ru_base/models/res_config_settings.py:0
#: model:ir.model.fields,field_description:l10n_ru_base.field_res_config_settings__module_contract
#, python-format
msgid "Contract"
msgstr "Договор"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "Contracts"
msgstr "Договоры"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "Powers of attorney"
msgstr "Доверенность на получение ТМЦ"
#. module: l10n_ru_base
#. odoo-python
#: code:addons/l10n_ru_base/models/res_config_settings.py:0
#: model:ir.model.fields,field_description:l10n_ru_base.field_res_config_settings__module_l10n_ru_doc
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
#, python-format
msgid "Print forms"
msgstr "Печатные формы"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "Report act revise"
msgstr "Отчет Акт сверки"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "Report contract"
msgstr "Договоры"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "Report report_xml"
msgstr "УПД в формате xml для ЭДО"
#. module: l10n_ru_base
#. odoo-python
#: code:addons/l10n_ru_base/models/res_config_settings.py:0
#: model:ir.model.fields,field_description:l10n_ru_base.field_res_config_settings__module_report_xml
#, python-format
msgid "Report_xml"
msgstr "Универсальный передаточный документ (УПД) в формате xml для ЭДО"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "Russian Localization"
msgstr "Российская локализация"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "UPD xml"
msgstr "УПД в формате xml для ЭДО"
#. module: l10n_ru_base
#: model_terms:ir.ui.view,arch_db:l10n_ru_base.res_config_settings_view_form
msgid "print_forms"
msgstr "Печатные формы документов"

View File

@ -0,0 +1,4 @@
from . import res_config_settings

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
from odoo.exceptions import UserError
import logging
_FIELDS_MODUL = [
'module_l10n_ru_act_rev',
'module_l10n_ru_contract',
'module_l10n_ru_upd_xml',
'module_l10n_ru_doc',
'module_l10n_ru_attorney'
]
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
module_l10n_ru_act_rev = fields.Boolean(_("Act revise"))
module_l10n_ru_contract = fields.Boolean(_("Contract"))
module_l10n_ru_upd_xml = fields.Boolean(_("Report_xml"))
module_l10n_ru_doc = fields.Boolean(_("Print forms"))
module_l10n_ru_attorney = fields.Boolean(_("Consent"))
@api.model
def write(self, values):
if any(field in values for field in _FIELDS_MODUL):
company = self.env.company
if company.country_id.code != 'RU':
raise UserError("Признак Российской компании не обнаружен!")
if _FIELDS_MODUL:
missing_modules = set()
for field in _FIELDS_MODUL:
mapped_values = self.mapped(field)
if mapped_values and mapped_values[0]:
module_name = field[7:]
module_installed = self.env['ir.module.module'].search([('name', '=', module_name)], limit=1)
if not module_installed:
missing_modules.add(module_name)
if missing_modules:
message = "Обратитесь в тех.поддержку для получения лицензии для следующих модулей:\n" + \
"\n".join(missing_modules)
raise UserError(message)
return super(ResConfigSettings, self).write(values)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.russian.localization</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="5"/>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//form" position="inside">
<app data-string="Russian Localization" string="Russian Localization" name="base_russian_localization" img="l10n_ru_base/static/description/icon.png">
<block title="Act revise" name="act_revise_setting_container">
<setting id="act_revise" string="Act revise" help="Report act revise">
<field name="module_l10n_ru_act_rev"/>
</setting>
</block>
<block title="Contracts" name="contract_setting_container">
<setting id="contract" string="Contracts" help="Report contract">
<field name="module_l10n_ru_contract"/>
</setting>
</block>
<block title="UPD xml" name="upd_xml_setting_container">
<setting id="report_xml" string="UPD xml" help="Report report_xml">
<field name="module_l10n_ru_upd_xml"/>
</setting>
</block>
<block title="Print forms" name="print_forms_setting_container">
<setting id="print_forms" string="Print forms" help="print_forms">
<field name="module_l10n_ru_doc"/>
</setting>
</block>
<block title="Consent" name="powers_attorney_setting_container">
<setting id="powers_attorney" string="Consent" help="Powers of attorney">
<field name="module_l10n_ru_attorney"/>
</setting>
</block>
</app>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,30 @@
# Российская локализация - Договоры
name: l10n_ru_contract
## Описание
Создание договоров с клиентами и поставщиками в системе.
Возможность разделения договоров на виды, отслеживание статуса договора. Вы также можете распечатать договор из системы.
### Создание вида договора клиента(поставщика):
1. Меню Продажи (Покупки) - Договоры - Виды договора - кнопка "Создать";
2. На форме указываем:
2.1. Журнал и счета дебетовой и кредиторской задолженности;
2.2. Присваиваем новое название.
### Создание договора клиента (поставщика):
1. Меню Продажи (Покупки) - Договоры - кнопка "Создать";
2. На форме указываем основные и дополнительные условия договора:
2.1. Контрагент - клиент (поставщик);
2.2. Тип контрагента;
2.3. Компанию, от лица которой будет подписан договор;
2.4. Вид договора.
### Для печати:
1. Открываем созданную запись договора - Действие - "Договор".

View File

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

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
{
'name': 'Российская локализация - Договоры',
'summary': """
Создание договоров, их видов и печать
""",
'description': """
Создание договоров с клиентами и поставщиками. Возможность разделения на виды договоров, отслеживание статуса договора и его печатью
Создание вида договора клиента(поставщика):
1. Меню Продажи (Покупки) - Договоры - Виды договора - кнопка "Создать";
2. На форме указываем:
2.1. Журнал и счета дебетовой и кредиторской задолженности;
2.2. Присваиваем новое название.
Создание договора клиента (поставщика):
1. Меню Продажи (Покупки) - Договоры - кнопка "Создать";
2. На форме указываем основные и дополнительные условия договора:
2.1. Контрагент - клиент (поставщик);
2.2. Тип контрагента;
2.3. Компанию, от лица которой будет подписан договор;
2.4. Вид договора.
Для печати:
1. Открываем созданную запись договора - Действие - "Договор".
""",
'version': '18.0.1.0.0',
'sequence': 0,
'author': 'MK.Lab',
'website': 'https://www.inf-centre.ru/',
'external_dependencies': {
'python': ['pymorphy2'],
},
'depends': [
'base',
'mail',
'account', 'sale', 'sale_management', 'purchase', 'l10n_ru_base'
],
'data': [
'data/data.xml',
'views/contract_customer_view.xml',
'security/ir.model.access.csv',
'report/report_contract.xml',
'report/report_contract_order.xml',
'report/report_contract_order1.xml',
'report/report_contract_invoice.xml',
],
'installable': True,
'auto_install': False,
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="1">
<record id="contract_customer_sequence" model="ir.sequence">
<field name="name">Договор последовательность клиент</field>
<field name="code">partner.contract.customer.sequence</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
<field eval="True" name="use_date_range"/>
<field name="padding">5</field>
</record>
<record id="contract_supplier_sequence" model="ir.sequence">
<field name="name">Договор последовательность поставщик</field>
<field name="code">partner.contract.supplier.sequence</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
<field eval="True" name="use_date_range"/>
<field name="padding">5</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
from . import account_move
from . import contract_customer
from . import crutch_fields_header
from . import dop_field
from . import purchase_order
from . import sale_make_invoice_advance
from . import sale_order

View File

@ -0,0 +1,50 @@
from odoo import api, fields, models, exceptions, _
class AccountMove(models.Model):
_inherit = 'account.move'
mt_contract_id = fields.Many2one('partner.contract.customer', string=_('Номер договора'))
sf_number = fields.Char(string=_('Номер с'))
osnovanie = fields.Char(string=_('Основание'))
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент'), store=True, compute='_compute_get_pid')
stamp = fields.Boolean(string=_('Печать и подпись'), related='mt_contract_id.stamp')
@api.depends('partner_id')
def _compute_get_pid(self):
for s in self:
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
@api.onchange('mt_contract_id')
def set_ons(self):
if self.mt_contract_id:
self.osnovanie = 'Договор № ' + self.mt_contract_id.name + ' от ' + fields.Datetime.from_string(
self.mt_contract_id.date_start).strftime("%d.%m.%Y")
@api.constrains('state')
def invoice_fields_check(self):
for s in self:
if s.state == 'posted':
if s.mt_contract_id:
errors_list = []
journal_in_contract = s.mt_contract_id.profile_id.journal_id
payment_term_in_contract = s.mt_contract_id.profile_id.payment_term_id
receivable_in_contract = s.mt_contract_id.profile_id.receivable_account_id
if journal_in_contract != s.journal_id:
errors_list.append(f'Отличается Журнал - [{s.journal_id.name}] '
f'и указанный в договоре №{s.mt_contract_id.name} '
f'Журнал - [{journal_in_contract.name}]\n\n')
if payment_term_in_contract != s.invoice_payment_term_id:
errors_list.append(f'Отличается поле "Условие оплаты" в инвойсе '
f'[Условие оплаты - {s.invoice_payment_term_id.name}] '
f'и указанный в договоре №{s.mt_contract_id.name} '
f'[Условие оплаты - {payment_term_in_contract.name}]\n\n')
if receivable_in_contract not in s.line_ids.account_id:
errors_list.append(f'Отличается поле "Счет дебиторской задолженности" в инвойсе '
f'и указанный в договоре №{s.mt_contract_id.name}')
if errors_list:
raise exceptions.ValidationError(''.join(errors_list))

View File

@ -0,0 +1,334 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, exceptions, tools, _
import pymorphy2
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from .crutch_fields_header import IP_CONTACT_HEADER, ENTITY_CONTRACT_HEADER, INDIVIDUAL_CONTRACT_HEADER
class PartnerContractCustomer(models.Model):
_name = 'partner.contract.customer'
_inherit = ['mail.thread', 'mail.activity.mixin', 'mail.render.mixin']
def get_dateend(self):
if self.date_start:
six_months = fields.Datetime.from_string(self.date_start) + relativedelta(months=+11)
else:
six_months = datetime.today() + relativedelta(months=+11)
return fields.Datetime.to_string(six_months)
name = fields.Char(string=_('Номер'))
date_start = fields.Date(string=_('Дата договора'), required=True, default=fields.Datetime.now())
partner_id = fields.Many2one('res.partner', string=_('Контрагент'), required=True)
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент как в заказе'))
company_id = fields.Many2one('res.company', string=_('Компания'), required=True)
name_print = fields.Char(string=_('Имя для печати'), compute='_get_name_print')
name_print1 = fields.Char(string=_('Имя для печати, И.П.'), compute='_get_name_printip')
date_end = fields.Date(string=_('Дата окончания'), required=True, default=get_dateend)
name_dirprint = fields.Char(string=_('Имя нашего директора для печати'))
name_dirprint1 = fields.Char(string=_('Имя нашего директора для печати И.П.'))
lines = fields.One2many('contract.line', 'contract_id', string=_('Договорные цены'))
type = fields.Selection(
[('customer', 'С покупателем'),
('supplier', 'С поставщиком'),
('other', 'Прочие расчеты'),
],
string=_('Тип договора'), default='customer', required=True)
saleorder_id = fields.Many2one('sale.order', string=_('Заказ/Сделка'))
stamp = fields.Boolean(string=_('Печать и подпись'))
signed = fields.Boolean(string=_('Договор подписан'))
state = fields.Selection(
[('draft', 'Черновик'),
('progress', 'На согласовании'),
('signed', 'Подписан, действует'),
('closed', 'Истёк'),
],
string=_('Статус'), default='draft', group_expand='_expand_states', index=True
)
is_template = fields.Boolean(_('Это шаблон'))
copy_from = fields.Many2one('partner.contract.customer', string=_('Копировать из этого шаблона'))
profile_id = fields.Many2one('contract.profile', string=_('Вид договора'), required=True)
credit_limit = fields.Float(string=_('Лимит кредита'))
guid_1s = fields.Char(_('Код договора из 1С'))
buh_code = fields.Char(_('Код договора из бухгалтерии'))
payment_term_id = fields.Many2one('account.payment.term', string=_('Условие оплаты'))
manager_id = fields.Many2one('res.users', string=_('Менеджер по продажам'))
accountant_id = fields.Many2one('res.users', string=_('Бухгалтер по взаиморасчетам'))
time_to_delivery_from = fields.Datetime(_('Время доставки от'))
time_to_delivery_to = fields.Datetime(_('Время доставки до'))
day_of_delivery = fields.Float(_('Дни доставки'))
day_of_otgruzki = fields.Float(_('Дни отгрузки'))
channel_id = fields.Many2one('saleorder.channel', string=_('Канал продаж'))
team_id = fields.Many2one('crm.team', string=_('Команда продаж'))
order_days_ids = fields.Many2many(comodel_name='contract.day', relation='orderdays', string=_('Дни доставки'),
column1='contract_id', column2='day_id')
shipment_days_ids = fields.Many2many(comodel_name='contract.day', relation='shipmentdays', string=_('Дни отгрузки'),
column1='contract_id',
column2='day_id')
# Доработка хедера договора
partner_type = fields.Selection(string=_('Тип контрагента'), selection=[
('person', 'Физ. лицо'),
('company_ip', 'ИП'),
('company', 'Юр. лицо')
], required=True)
contract_header = fields.Html(_('Шапка договора'))
@api.onchange('partner_type')
def generate_contract_header(self):
self.ensure_one()
self.render_model = 'partner.contract.customer'
if self.partner_type == 'company_ip':
self.contract_header = IP_CONTACT_HEADER
elif self.partner_type == 'person':
self.contract_header = INDIVIDUAL_CONTRACT_HEADER
else:
self.contract_header = ENTITY_CONTRACT_HEADER
# # Рендер Jinja выражение типа {{object.field}}
result = self._render_template(self.contract_header, self.render_model, res_ids=[self.id])
result = tools.html_sanitize(result[self.id])
self.contract_header = result
@api.onchange('sec_partner_id')
def set_pid(self):
self.partner_id = self.sec_partner_id.parent_id if self.sec_partner_id.parent_id else self.sec_partner_id
def _expand_states(self, states, domain):
return [key for key, val in type(self).state.selection]
def copy_it(self):
if self.copy_from:
for line in self.copy_from.lines:
line.copy({'contract_id': self.id})
def _get_name_print(self):
morph = pymorphy2.MorphAnalyzer()
self.name_print = False
director = self.env['res.partner'].search([('parent_id', '=', self.partner_id.id), ('type', '=', 'director')],
limit=1)
if director:
if len(director.name.split(' ')) == 3:
lastname_old, firstname_old, middlename_old = director.name.split(' ')
if lastname_old:
lastname_n = morph.parse(lastname_old)[0]
if lastname_n.inflect({'gent'}):
lastname_n = lastname_n.inflect({'gent'}).word
else:
lastname_n = lastname_old
else:
lastname_n = ''
if firstname_old:
firstname_n = morph.parse(firstname_old)[0]
firstname_n = firstname_n.inflect({'gent'}).word
else:
firstname_n = ''
if middlename_old:
middlename_n = morph.parse(middlename_old)[0]
middlename_n = middlename_n.inflect({'gent'}).word
else:
middlename_n = ''
name_print = lastname_n + ' ' + firstname_n + ' ' + middlename_n
self.name_print = name_print.title()
def _get_name_print1(self):
# morph = pymorphy2.MorphAnalyzer()
director = self.company_id.chief_id.partner_id if self.company_id.chief_id else False
# raise exceptions.UserError(str(director))
self.name_dirprint = False
if director:
if len(director.name.split(' ')) == 3:
lastname_old, firstname_old, middlename_old = director.name.split(' ')
if lastname_old:
lastname_n = morph.parse(lastname_old)[0]
lastname_n = lastname_n.inflect({'gent'}).word
else:
lastname_n = ''
if firstname_old:
firstname_n = morph.parse(firstname_old)[0]
firstname_n = firstname_n.inflect({'gent'}).word
else:
firstname_n = ''
if middlename_old:
middlename_n = morph.parse(middlename_old)[0]
middlename_n = middlename_n.inflect({'gent'}).word
else:
middlename_n = ''
name_print = lastname_n + ' ' + firstname_n + ' ' + middlename_n
self.name_dirprint = name_print.title()
@api.model
def create(self, values):
res = super(PartnerContractCustomer, self).create(values)
if values.get('is_template'):
return res
if values.get('type') == 'customer':
sequence_code = 'partner.contract.customer.sequence'
elif values.get('type') == 'supplier':
sequence_code = 'partner.contract.supplier.sequence'
else:
return res
name = self.env['ir.sequence'].next_by_code(sequence_code)
res.update({
'name': name,
})
return res
# @api.model
def write(self, values):
if 'state' in values:
if self.state != values['state']:
msg = 'Статус: ' + dict(self._fields['state'].selection).get(self.state) + ' -> ' + dict(
self._fields['state'].selection).get(values['state'])
self.message_post(body=msg)
res = super(PartnerContractCustomer, self).write(values)
return res
def _get_name_print1ip(self):
self.name_dirprint1 = self.company_id.chief_id.partner_id.name if self.company_id.chief_id else False
def _get_name_printip(self):
self.name_print1 = False
director = self.env['res.partner'].search([('parent_id', '=', self.partner_id.id), ('type', '=', 'director')],
limit=1)
if director:
self.name_print1 = director.name
def print_supp(self):
# self.filtered(lambda s: s.state == 'draft').write({'state': 'sent'})
return self.env['report'].get_action(self, 'mta_base.action_mtacontractsupp_report')
def print_contract_cust(self):
if self.saleorder_id:
return self.saleorder_id.print_contract()
else:
raise exceptions.UserError(_(
'Вы не можете напечатать договор с Клиентом, потому что нет связи с Заказом. Нужно зайти в Заказ и привязать этот договор.'))
def contract_action_confirm(self):
if self.state == 'draft':
self.state = 'progress'
elif self.state == 'progress':
self.state = 'signed'
def contract_in_draft(self):
self.state = 'draft'
@api.onchange('name')
def set_comp_and_partn(self):
context = self._context
order_id = context.get('sale_order_id')
if order_id:
sale_order = self.env['sale.order'].browse(order_id)
self.company_id = sale_order.company_id
self.partner_id = sale_order.partner_id
@api.onchange('profile_id')
def set_payment(self):
if self.profile_id.payment_term_id:
self.payment_term_id = self.profile_id.payment_term_id
# @api.constrains('name')
def check_name(self):
obj = self.search([('name', '=', self.name), ('id', '!=', self.id), ('state', '!=', 'closed')])
if obj:
raise exceptions.ValidationError(_('Договор с таким номером уже существует'))
"""
@api.constrains('profile_id')
def check_profile_id(self):
contracts = self.search([('partner_id', '=', self.partner_id.id), ('id', '!=', self.id)])
if contracts:
profiles_in_contracts = contracts.profile_id
# raise exceptions.ValidationError(profiles_in_contracts.ids)
if profiles_in_contracts:
ads = self.env['contract.allowed.profiles'].search(
[('allowed_profiles', 'in', profiles_in_contracts.ids)])
if ads:
raise exceptions.ValidationError((self.profile_id.name, ads.name))
# raise exceptions.ValidationError(contracts)
"""
class Partner(models.Model):
_inherit = 'res.partner'
contract_count = fields.Integer(string=_('Договоры'), compute='get_count_contract')
pol = fields.Selection(string=_("Пол"), selection=[('m', 'Муж.'), ('j', 'Жен'), ], required=False)
type = fields.Selection(selection_add=[('director', 'Директор')])
def get_count_contract(self):
contract = self.env['partner.contract.customer']
self.contract_count = contract.search_count([('partner_id', '=', self.id)])
def action_view_contract(self):
action = self.env.ref('contract.contract_customer_action').read()[0]
action['domain'] = [('partner_id', '=', self.id)]
return action
class ContractLine(models.Model):
_name = 'contract.line'
contract_id = fields.Many2one('partner.contract.customer', string='Order Reference', required=True,
ondelete='cascade', index=True, copy=False)
_order = "sequence desc"
# name = fields.Text(string='Название для договора')
# price_unit = fields.Float('Цена', default=0.0)
# product_uom = fields.Many2one('uom.uom', string='Единица измерения')
# product_id = fields.Many2one('product.product', string='Услуга', domain=[('sale_ok', '=', True)], change_default=True, ondelete='restrict')
sequence = fields.Integer('Порядок')
name = fields.Char('Номер пункта')
punct = fields.Html('Текст пункта')
@api.onchange('product_id')
def set_name(self):
self.name = self.product_id.name
class AllowedProfiles(models.Model):
_name = 'contract.allowed.profiles'
name = fields.Char(string=_('Одновременно включены следующие виды договоров:'))
allowed_profiles = fields.Many2many('contract.profile', string=_('Виды договоров'), required=True)
@api.onchange('allowed_profiles')
def set_name(self):
self.name = ''
for profile in self.allowed_profiles:
self.name += profile.name + ' + '
if self.name:
if self.name[-2] == '+':
self.name = self.name[:-2]
class ContractProfile(models.Model):
_name = 'contract.profile'
name = fields.Char(string=_('Вид договора'), required=True)
payable_account_id = fields.Many2one('account.account', string=_('Счет кредиторской задолженности'), required=True)
receivable_account_id = fields.Many2one('account.account', string=_('Счет дебиторской задолженности'), required=True)
max_receivable_id = fields.Float(string=_('Максимальная деб. задолженность'), required=True)
payment_term_id = fields.Many2one('account.payment.term', string=_('Условие оплаты'), required=True)
journal_id = fields.Many2one('account.journal', string=_('Журнал'), required=True)
class ContractDay(models.Model):
_name = 'contract.day'
name = fields.Char(_('День'))

View File

@ -0,0 +1,33 @@
ENTITY_CONTRACT_HEADER = """
<b>{{object.company_id.partner_id.name}}</b>,
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
{{(object.company_id.chief_id.partner_id.function or '').lower()}}
{{(object.name_dirprint1 or '').title()}},
действующего на основании ОГРНИП {{object.company_id.company_registry or ''}}, с одной стороны, и <b>{{object.partner_id.name or ''}}</b>,
именуемое в дальнейшем <b>«Покупатель»</b>, в лице
{{(object.get_function_partner1(object.partner_id.id) or '').lower()}}
{{(object.name_print1 or '').title()}}, действующего на основании устава общества, с другой стороны, вместе именуемые в дальнейшем <b>«Стороны»</b> заключили
настоящий Договор о нижеследующем:
"""
IP_CONTACT_HEADER = """
<b>{{object.company_id.partner_id.name}}</b>,
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
{{(object.company_id.chief_id.partner_id.function or '').lower()}}
{{(object.name_dirprint1 or '').title()}},
действующего на основании ОГРНИП {{object.company_id.company_registry or ''}}, с одной стороны, и <b>{{object.partner_id.name or ''}}</b>,
именуемое в дальнейшем <b>«Покупатель»</b>, в лице
{{(object.get_function_partner1(object.partner_id.id) or '').lower()}}
{{(object.name_print1 or '').title()}}, действующего на основании ОГРНИП {{object.partner_id.ogrn or ''}},
с другой стороны, вместе именуемые в дальнейшем <b>«Стороны»</b> заключили
настоящий Договор о нижеследующем:
"""
INDIVIDUAL_CONTRACT_HEADER = """
<b>{{object.company_id.partner_id.name}}</b>,
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
{{(object.company_id.chief_id.partner_id.function or '').lower()}}
{{(object.name_dirprint1 or '').title()}},
действующего на основании ОГРНИП {{object.company_id.company_registry or ''}}, с одной стороны, и <b>{{object.partner_id.name or ''}}</b>,
именуемое в дальнейшем <b>«Покупатель»</b>, вместе именуемые в дальнейшем <b>«Стороны»</b> заключили
настоящий Договор о нижеследующем:
"""

View File

@ -0,0 +1,353 @@
from odoo import api, fields, models, exceptions
from datetime import datetime
import re
import pymorphy2
from odoo.tools import pycompat
FRACTIONS = (
(u"десятая", u"десятых", u"десятых"),
(u"сотая", u"сотых", u"сотых"),
(u"тысячная", u"тысячных", u"тысячных"),
(u"десятитысячная", u"десятитысячных", u"десятитысячных"),
(u"стотысячная", u"стотысячных", u"стотысячных"),
(u"миллионная", u"милллионных", u"милллионных"),
(u"десятимиллионная", u"десятимилллионных", u"десятимиллионных"),
(u"стомиллионная", u"стомилллионных", u"стомиллионных"),
(u"миллиардная", u"миллиардных", u"миллиардных"),
)
ONES = {
0: (u"", u"", u""),
1: (u"один", u"одна", u"одно"),
2: (u"два", u"две", u"два"),
3: (u"три", u"три", u"три"),
4: (u"четыре", u"четыре", u"четыре"),
5: (u"пять", u"пять", u"пять"),
6: (u"шесть", u"шесть", u"шесть"),
7: (u"семь", u"семь", u"семь"),
8: (u"восемь", u"восемь", u"восемь"),
9: (u"девять", u"девять", u"девять"),
}
TENS = {
0: u"",
10: u"десять",
11: u"одиннадцать",
12: u"двенадцать",
13: u"тринадцать",
14: u"четырнадцать",
15: u"пятнадцать",
16: u"шестнадцать",
17: u"семнадцать",
18: u"восемнадцать",
19: u"девятнадцать",
2: u"двадцать",
3: u"тридцать",
4: u"сорок",
5: u"пятьдесят",
6: u"шестьдесят",
7: u"семьдесят",
8: u"восемьдесят",
9: u"девяносто",
}
HUNDREDS = {
0: u"",
1: u"сто",
2: u"двести",
3: u"триста",
4: u"четыреста",
5: u"пятьсот",
6: u"шестьсот",
7: u"семьсот",
8: u"восемьсот",
9: u"девятьсот",
}
MALE = 1
FEMALE = 2
import operator
import sys
import types
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
MAXSIZE = sys.maxsize
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
class Partner_Bank(models.Model):
_inherit = 'res.partner.bank'
bank_corr_acc = fields.Char('Кор.счет')
# class Bank(models.Model):
# _inherit = 'res.bank'
# corr_acc = fields.Char('Corresponding account', size=64)
# class Users(models.Model):
# _inherit = 'res.users'
# facsimile = fields.Binary("Facsimile")
class Company(models.Model):
_inherit = 'res.company'
inn = fields.Char(related='partner_id.inn', readonly=False)
kpp = fields.Char(related='partner_id.kpp', readonly=False)
okpo = fields.Char(related='partner_id.okpo', readonly=False)
chief_id = fields.Many2one('res.users', 'Имя директора')
stamp = fields.Binary("Stamp")
class Partner(models.Model):
_inherit = 'res.partner'
ogrn = fields.Char('ОГРН')
okpo = fields.Char('ОКПО')
inn = fields.Char('ИНН')
kpp = fields.Char('KPP')
passport = fields.Char('Паспорт')
class Report_contract_customer(models.Model):
_inherit = 'partner.contract.customer'
def img(self, img, type='png', width=0, height=0):
if width:
width = "width='%spx'" % (width)
else:
width = " "
if height:
height = "height='%spx'" % (height)
else:
height = " "
toreturn = "<img %s %s src='data:image/%s;base64,%s' />" % (
width,
height,
type,
str(pycompat.to_text(img)))
return toreturn
def numer(self, name):
if name:
numeration = re.findall('\d+$', name)
if numeration: return numeration[0]
return ''
def ru_date(self, date):
if date and date != 'False':
return dt.ru_strftime(u'"%d" %B %Y года', date=datetime.strptime(str(date), "%Y-%m-%d"), inflected=True)
return ''
def ru_date2(self, date):
if date and date != 'False':
return dt.ru_strftime(u'%d %B %Y г.', date=datetime.strptime(str(date), "%Y-%m-%d %H:%M:%S"),
inflected=True)
return ''
def in_words(self, number):
return numeral.in_words(number)
def rubles(self, sum):
"Transform sum number in rubles to text"
text_rubles = self.numeral_rubles(int(sum))
copeck = round((sum - int(sum)) * 100)
text_copeck = self.numeral_choose_plural(int(copeck), (u"копейка", u"копейки", u"копеек"))
return ("%s %02d %s") % (text_rubles, copeck, text_copeck)
def numeral_rubles(self, amount, zero_for_kopeck=False):
self.check_positive(amount)
pts = []
amount = round(amount, 2)
pts.append(self.sum_string(int(amount), 1, (u"рубль", u"рубля", u"рублей")))
remainder = self._get_float_remainder(amount, 2)
iremainder = int(remainder)
if iremainder != 0 or zero_for_kopeck:
if iremainder < 10 and len(remainder) == 1:
iremainder *= 10
pts.append(self.sum_string(iremainder, 2,
(u"копейка", u"копейки", u"копеек")))
return u" ".join(pts)
def _get_float_remainder(self, fvalue, signs=9):
self.check_positive(fvalue)
if isinstance(fvalue, integer_types):
return "0"
if isinstance(fvalue, Decimal) and fvalue.as_tuple()[2] == 0:
return "0"
def sum_string(self, amount, gender, items=None):
if isinstance(items, text_type):
items = split_values(items)
if items is None:
items = (u"", u"", u"")
try:
one_item, two_items, five_items = items
except ValueError:
raise ValueError("Items must be 3-element sequence")
self.check_positive(amount)
if amount == 0:
return u"ноль %s" % five_items
into = u''
tmp_val = amount
into, tmp_val = self._sum_string_fn(into, tmp_val, gender, items)
into, tmp_val = self._sum_string_fn(into, tmp_val, FEMALE,
(u"тысяча", u"тысячи", u"тысяч"))
into, tmp_val = self._sum_string_fn(into, tmp_val, MALE,
(u"миллион", u"миллиона", u"миллионов"))
into, tmp_val = self._sum_string_fn(into, tmp_val, MALE,
(u"миллиард", u"миллиарда", u"миллиардов"))
if tmp_val == 0:
return into
else:
raise ValueError("Cannot operand with numbers bigger than 10**11")
def _sum_string_fn(self, into, tmp_val, gender, items=None):
if items is None:
items = (u"", u"", u"")
one_item, two_items, five_items = items
self.check_positive(tmp_val)
if tmp_val == 0:
return into, tmp_val
words = []
rest = tmp_val % 1000
tmp_val = tmp_val // 1000
if rest == 0:
if into == u"":
into = u"%s " % five_items
return into, tmp_val
end_word = five_items
words.append(HUNDREDS[rest // 100])
rest = rest % 100
rest1 = rest // 10
tens = rest1 == 1 and TENS[rest] or TENS[rest1]
words.append(tens)
if rest1 < 1 or rest1 > 1:
amount = rest % 10
end_word = self.numeral_choose_plural(amount, items)
words.append(ONES[amount][gender - 1])
words.append(end_word)
words.append(into)
words = filter(lambda x: len(x) > 0, words)
return u" ".join(words).strip(), tmp_val
def check_positive(self, value, strict=False):
if not strict and value < 0:
raise ValueError("Value must be positive or zero, not %s" % str(value))
if strict and value <= 0:
raise ValueError("Value must be positive, not %s" % str(value))
def numeral_choose_plural(self, amount, variants):
if isinstance(variants, text_type):
variants = split_values(variants)
self.check_length(variants, 3)
amount = abs(amount)
if amount % 10 == 1 and amount % 100 != 11:
variant = 0
elif amount % 10 >= 2 and amount % 10 <= 4 and \
(amount % 100 < 10 or amount % 100 >= 20):
variant = 1
else:
variant = 2
return variants[variant]
def check_length(self, value, length):
_length = len(value)
if _length != length:
raise ValueError("length must be %d, not %d" % \
(length, _length))
def initials(self, fio):
if fio:
return (fio.split()[0] + ' ' + ''.join([fio[0:1] + '.' for fio in fio.split()[1:]])).strip()
return ''
def address(self, partner):
repr = []
if partner.zip: repr.append(partner.zip)
if partner.city: repr.append(partner.city)
if partner.street: repr.append(partner.street)
if partner.street2: repr.append(partner.street2)
return ', '.join(repr)
def address_delivery(self, partner):
if partner:
addr = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'delivery')], limit=1)
repr = []
if addr:
if addr.zip: repr.append(addr.zip)
if addr.city: repr.append(addr.city)
if addr.street: repr.append(addr.street)
if addr.street2: repr.append(addr.street2)
return ', '.join(repr)
def get_function_print(self, function):
morph = pymorphy2.MorphAnalyzer()
if function:
f = morph.parse(function)[0]
f = f.inflect({'gent'}).word
return f.title()
def get_function_partnerip(self, partner):
director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
if director:
if director.function:
return director.function
def get_function_partner(self, partner):
res = []
morph = pymorphy2.MorphAnalyzer()
if partner:
director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
if director:
if director.function:
list_f = str(director.function).split(' ')
for func in list_f:
f = morph.parse(func)[0]
f = f.inflect({'gent'}).word
res.append(f)
return ' '.join(res)
def get_function_partner1(self, partner):
if partner:
director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
if director:
if director.function:
return director.function
def get_date_text(self, date):
month_list = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября',
'ноября', 'декабря']
if date:
date_list = str(date).split('-')
if date_list[0] and date_list[1] and date_list[2]:
return ('"' + date_list[2] + '" ' + month_list[int(date_list[1]) - 1] + ' ' + date_list[0] + ' г.')
def get_bank(self, partner):
repr = []
bank = None
if partner.bank_ids:
bank = partner.bank_ids[0]
elif partner.parent_id.bank_ids:
bank = partner.parent_id.bank_ids[0]
if bank and bank.bank_name: repr.append(bank.bank_name)
if bank and bank.acc_number: repr.append(u"Р/счет " + bank.acc_number)
if bank and bank.bank_bic: repr.append(u"БИК " + bank.bank_bic)
if bank and bank.bank_corr_acc: repr.append(u"к/с " + bank.bank_corr_acc)
return '<br/>'.join(repr)

View File

@ -0,0 +1,13 @@
from odoo import api, fields, models, exceptions, _
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
mt_contract_id = fields.Many2one('partner.contract.customer', string=_('Номер договора'))
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент'), store=True, compute='_compute_get_pid')
@api.depends('partner_id')
def _compute_get_pid(self):
for s in self:
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id

View File

@ -0,0 +1,18 @@
from odoo import api, fields, models
class ContractCreateInvoice(models.TransientModel):
_inherit = 'sale.advance.payment.inv'
# # при выбора счета "Авансовый платеж"
# @api.model
# def _create_invoice(self, order, so_line, amount):
# res = super(ContractCreateInvoice, self)._create_invoice(order, so_line, amount)
# if order.mt_contract_id:
# res.write({'mt_contract_id': order.mt_contract_id,
# 'journal_id': order.mt_contract_id.profile_id.journal_id, })
# return res
def _prepare_invoice_values(self, order, name, amount):
invoice_vals = super(ContractCreateInvoice, self)._prepare_invoice_values(order, name, amount)
invoice_vals['mt_contract_id'] = order.mt_contract_id.id
return invoice_vals

View File

@ -0,0 +1,62 @@
from odoo import api, fields, models, exceptions, _
from datetime import datetime
class SaleOrder(models.Model):
_inherit = 'sale.order'
mt_contract_id = fields.Many2one('partner.contract.customer', string=_('Номер договора'))
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент'), store=True, compute='_compute_get_pid')
stamp = fields.Boolean(string=_('Печать и подпись'), related='mt_contract_id.stamp')
@api.depends('partner_id')
def _compute_get_pid(self):
for s in self:
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
@api.onchange('mt_contract_id')
def set_ons(self):
if self.mt_contract_id:
self.payment_term_id = self.mt_contract_id.payment_term_id
@api.constrains('state')
def late_payment_check(self):
if self.mt_contract_id:
if self.state == 'sale':
late_invoices_count = 0
max_receivable = self.mt_contract_id.profile_id.max_receivable_id # макс. деб. задолженность в договоре
# ищу просроченные инвойсы контрагента указанного в заказе со стейтом "Подтверждено"
invoices_obj = self.env['account.move'].search([('partner_id', '=', self.partner_id.id),
('state', '=', 'posted'),
('invoice_date_due', '<', datetime.now().date())])
for invoice in invoices_obj:
late_invoices_count += invoice.amount_residual # складываю деб. задолженность по просроченным инвойсам
if late_invoices_count > max_receivable:
raise exceptions.ValidationError(
f'Нельзя подтвердить заказ, так как у контрагента {self.sec_partner_id.name} нарушено '
f'условие по дебиторской задолженности.\n\n'
f'Контрагент {self.sec_partner_id.name} должен {late_invoices_count}руб.\n'
f'Максимальная дебиторская задолженность указанная в '
f'договоре №{self.mt_contract_id.name} - {max_receivable}руб.\n\n'
f'Проверьте следующие неоплаченные счета контрагента:\n'
f'{", ".join([invoice.name for invoice in invoices_obj])}')
# # при выбора счета "Обычный счет"
# @api.model
# def _create_invoices(self, grouped=False, final=False, date=None):
# res = super(SaleOrder, self)._create_invoices(grouped, final, date)
# if self.mt_contract_id:
# res.write({'mt_contract_id': self.mt_contract_id,
# 'journal_id': self.mt_contract_id.profile_id.journal_id})
# return res
def _get_invoice_grouping_keys(self):
res = super(SaleOrder, self)._get_invoice_grouping_keys()
res.append('mt_contract_id')
return res #['company_id', 'partner_id', 'currency_id', 'mt_contractid']
def _prepare_invoice(self):
invoice_vals = super(SaleOrder, self)._prepare_invoice()
invoice_vals['mt_contract_id'] = self.mt_contract_id.id
return invoice_vals

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from . import report_contract
from . import report_contract_order
from . import report_contract_invoice

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