mirror of
https://github.com/bringout/oca-ocb-accounting.git
synced 2026-04-23 18:42:02 +02:00
19.0 vanilla
This commit is contained in:
parent
ba20ce7443
commit
768b70e05e
2357 changed files with 1057103 additions and 712486 deletions
|
|
@ -4,7 +4,7 @@
|
|||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ReportAccountHashIntegrity(models.AbstractModel):
|
||||
class ReportAccountReport_Hash_Integrity(models.AbstractModel):
|
||||
_name = 'report.account.report_hash_integrity'
|
||||
_description = 'Get hash integrity result as PDF.'
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<div class="row" id="hash_header">
|
||||
<div class="col-12">
|
||||
<br/>
|
||||
<h2>Hash Integrity Result - <span t-esc="data['printing_date']"/></h2>
|
||||
<h2>Data Inalterability Check Report - <span t-out="data['printing_date']"/></h2>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -22,29 +22,29 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<table class="table table-bordered" style="table-layout: fixed" id="hash_config_table">
|
||||
<table class="table table-borderless" id="hash_config_table">
|
||||
<thead style="display: table-row-group">
|
||||
<tr>
|
||||
<th class="text-center" style="width: 30%" scope="col">Journal</th>
|
||||
<th class="text-center" style="width: 20%" scope="col">Inalterability check</th>
|
||||
<th class="text-center" style="width: 50%" scope="col">Coverage</th>
|
||||
<th style="width: 30%" scope="col">Journal (Sequence Prefix)</th>
|
||||
<th class="text-center text-nowrap" style="width: 20%" scope="col">Restricted</th>
|
||||
<th style="width: 50%" scope="col">Check</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="data['results']" t-as="result">
|
||||
<tr>
|
||||
<td>
|
||||
[<span t-esc="result['journal_code']"/>] <span t-esc="result['journal_name']"/>
|
||||
<span t-out="result['journal_name']"/>
|
||||
</td>
|
||||
<td class="text-center"><span t-esc="result['restricted_by_hash_table']"/></td>
|
||||
<td><span t-esc="result['msg_cover']"/></td>
|
||||
<td class="text-center"><span t-out="result['restricted_by_hash_table']"/></td>
|
||||
<td><span t-out="result['msg_cover']"/></td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="any(result['first_hash'] != 'None' for result in data['results'])">
|
||||
<t t-if="any(result['status'] == 'verified' for result in data['results'])">
|
||||
<div class="row" style="page-break-before:always;">
|
||||
<div class="col-12" id="hash_data_consistency">
|
||||
<br/>
|
||||
|
|
@ -54,30 +54,30 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" id="hash_data_consistency_table">
|
||||
<table class="table table-bordered" style="table-layout: fixed">
|
||||
<thead style="display: table-row-group">
|
||||
<table class="table table-borderless">
|
||||
<thead style="display: table-row-group; border-bottom: 1px solid black;">
|
||||
<tr>
|
||||
<th class="text-center" style="width: 20%" scope="col">Journal</th>
|
||||
<th class="text-center" style="width: 20%" scope="col">First Hash</th>
|
||||
<th class="text-center" style="width: 20%" scope="col">First Entry</th>
|
||||
<th class="text-center" style="width: 20%" scope="col">Last Hash</th>
|
||||
<th class="text-center" style="width: 20%" scope="col">Last Entry</th>
|
||||
<th class="text-start fw-bold" style="width: 20%" scope="col">Journal</th>
|
||||
<th class="text-start fw-bold" style="width: 20%" scope="col">First Hash</th>
|
||||
<th class="text-start fw-bold" style="width: 20%" scope="col">First Entry</th>
|
||||
<th class="text-start fw-bold" style="width: 20%" scope="col">Last Hash</th>
|
||||
<th class="text-start fw-bold" style="width: 20%" scope="col">Last Entry</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="data['results']" t-as="result">
|
||||
<t t-if="result['first_hash'] != 'None'">
|
||||
<tr>
|
||||
<td><span t-esc="result['journal_code']"/></td>
|
||||
<td><span t-esc="result['first_hash']"/></td>
|
||||
<td>
|
||||
<span t-esc="result['first_move_name']"/> <br/>
|
||||
<span t-esc="result['first_move_date']"/>
|
||||
<t t-if="result['status'] == 'verified'">
|
||||
<tr style="border-bottom: 1px solid lightgrey;">
|
||||
<td class="align-top"><span t-out="result['journal_name']"/></td>
|
||||
<td class="align-top text-break"><span t-out="result['first_hash']"/></td>
|
||||
<td class="align-top">
|
||||
<span t-out="result['first_move_name']"/> <br/>
|
||||
<span t-out="result['first_move_date']"/>
|
||||
</td>
|
||||
<td><span t-esc="result['last_hash']"/></td>
|
||||
<td>
|
||||
<span t-esc="result['last_move_name']"/> <br/>
|
||||
<span t-esc="result['last_move_date']"/>
|
||||
<td class="align-top text-break"><span t-out="result['last_hash']"/></td>
|
||||
<td class="align-top">
|
||||
<span t-out="result['last_move_name']"/> <br/>
|
||||
<span t-out="result['last_move_date']"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.tools import SQL
|
||||
from odoo.tools.query import Query
|
||||
from odoo.addons.account.models.account_move import PAYMENT_STATE_SELECTION
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class AccountInvoiceReport(models.Model):
|
||||
_name = "account.invoice.report"
|
||||
_name = 'account.invoice.report'
|
||||
_description = "Invoices Statistics"
|
||||
_auto = False
|
||||
_rec_name = 'invoice_date'
|
||||
|
|
@ -40,39 +42,44 @@ class AccountInvoiceReport(models.Model):
|
|||
# ==== Invoice line fields ====
|
||||
quantity = fields.Float(string='Product Quantity', readonly=True)
|
||||
product_id = fields.Many2one('product.product', string='Product', readonly=True)
|
||||
product_uom_id = fields.Many2one('uom.uom', string='Unit of Measure', readonly=True)
|
||||
product_uom_id = fields.Many2one('uom.uom', string='Unit', readonly=True)
|
||||
product_categ_id = fields.Many2one('product.category', string='Product Category', readonly=True)
|
||||
invoice_date_due = fields.Date(string='Due Date', readonly=True)
|
||||
account_id = fields.Many2one('account.account', string='Revenue/Expense Account', readonly=True, domain=[('deprecated', '=', False)])
|
||||
price_subtotal = fields.Float(string='Untaxed Total', readonly=True)
|
||||
price_total = fields.Float(string='Total in Currency', readonly=True)
|
||||
price_average = fields.Float(string='Average Price', readonly=True, group_operator="avg")
|
||||
account_id = fields.Many2one('account.account', string='Revenue/Expense Account', readonly=True)
|
||||
price_subtotal_currency = fields.Float(string='Untaxed Amount in Currency', readonly=True)
|
||||
price_subtotal = fields.Float(string='Untaxed Amount', readonly=True)
|
||||
price_total = fields.Float(string='Total', readonly=True)
|
||||
price_total_currency = fields.Float(string='Total in Currency', readonly=True)
|
||||
price_average = fields.Float(string='Average Price', readonly=True, aggregator="avg")
|
||||
price_margin = fields.Float(string='Margin', readonly=True)
|
||||
inventory_value = fields.Float(string='Inventory Value', readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True)
|
||||
|
||||
_depends = {
|
||||
'account.move': [
|
||||
'name', 'state', 'move_type', 'partner_id', 'invoice_user_id', 'fiscal_position_id',
|
||||
'invoice_date', 'invoice_date_due', 'invoice_payment_term_id', 'partner_bank_id',
|
||||
'invoice_date', 'invoice_date_due', 'invoice_payment_term_id', 'partner_bank_id', 'invoice_currency_rate',
|
||||
],
|
||||
'account.move.line': [
|
||||
'quantity', 'price_subtotal', 'price_total', 'amount_residual', 'balance', 'amount_currency',
|
||||
'move_id', 'product_id', 'product_uom_id', 'account_id',
|
||||
'journal_id', 'company_id', 'currency_id', 'partner_id',
|
||||
],
|
||||
'product.product': ['product_tmpl_id'],
|
||||
'product.product': ['product_tmpl_id', 'standard_price'],
|
||||
'product.template': ['categ_id'],
|
||||
'uom.uom': ['category_id', 'factor', 'name', 'uom_type'],
|
||||
'uom.uom': ['factor', 'name'],
|
||||
'res.currency.rate': ['currency_id', 'name'],
|
||||
'res.partner': ['country_id'],
|
||||
}
|
||||
|
||||
@property
|
||||
def _table_query(self):
|
||||
return '%s %s %s' % (self._select(), self._from(), self._where())
|
||||
def _table_query(self) -> SQL:
|
||||
return SQL('%s %s %s', self._select(), self._from(), self._where())
|
||||
|
||||
@api.model
|
||||
def _select(self):
|
||||
return '''
|
||||
def _select(self) -> SQL:
|
||||
return SQL(
|
||||
'''
|
||||
SELECT
|
||||
line.id,
|
||||
line.move_id,
|
||||
|
|
@ -93,24 +100,39 @@ class AccountInvoiceReport(models.Model):
|
|||
move.invoice_date_due,
|
||||
uom_template.id AS product_uom_id,
|
||||
template.categ_id AS product_categ_id,
|
||||
line.quantity / NULLIF(COALESCE(uom_line.factor, 1) / COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
AS quantity,
|
||||
-line.balance * currency_table.rate AS price_subtotal,
|
||||
line.price_subtotal * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
AS price_subtotal_currency,
|
||||
-line.balance * account_currency_table.rate AS price_subtotal,
|
||||
line.price_total * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
/ move.invoice_currency_rate
|
||||
AS price_total,
|
||||
line.price_total * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
AS price_total_currency,
|
||||
-COALESCE(
|
||||
-- Average line price
|
||||
(line.balance / NULLIF(line.quantity, 0.0)) * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
||||
-- convert to template uom
|
||||
* (NULLIF(COALESCE(uom_line.factor, 1), 0.0) / NULLIF(COALESCE(uom_template.factor, 1), 0.0)),
|
||||
0.0) * currency_table.rate AS price_average,
|
||||
/ NULLIF(COALESCE(uom_line.factor, 1), 0.0) * COALESCE(uom_template.factor, 1),
|
||||
0.0) * account_currency_table.rate AS price_average,
|
||||
CASE
|
||||
WHEN move.move_type NOT IN ('out_invoice', 'out_receipt', 'out_refund') THEN 0.0
|
||||
WHEN move.move_type = 'out_refund' THEN account_currency_table.rate * (-line.balance + (line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(COALESCE(uom_template.factor, 1), 0.0)) * COALESCE(product.standard_price -> line.company_id::text, to_jsonb(0.0))::float)
|
||||
ELSE account_currency_table.rate * (-line.balance - (line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(COALESCE(uom_template.factor, 1), 0.0)) * COALESCE(product.standard_price -> line.company_id::text, to_jsonb(0.0))::float)
|
||||
END
|
||||
AS price_margin,
|
||||
account_currency_table.rate * line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('out_invoice','in_refund','out_receipt') THEN -1 ELSE 1 END)
|
||||
* COALESCE(product.standard_price -> line.company_id::text, to_jsonb(0.0))::float AS inventory_value,
|
||||
COALESCE(partner.country_id, commercial_partner.country_id) AS country_id,
|
||||
line.currency_id AS currency_id
|
||||
'''
|
||||
''',
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _from(self):
|
||||
return '''
|
||||
def _from(self) -> SQL:
|
||||
return SQL(
|
||||
'''
|
||||
FROM account_move_line line
|
||||
LEFT JOIN res_partner partner ON partner.id = line.partner_id
|
||||
LEFT JOIN product_product product ON product.id = line.product_id
|
||||
|
|
@ -120,45 +142,33 @@ class AccountInvoiceReport(models.Model):
|
|||
LEFT JOIN uom_uom uom_template ON uom_template.id = template.uom_id
|
||||
INNER JOIN account_move move ON move.id = line.move_id
|
||||
LEFT JOIN res_partner commercial_partner ON commercial_partner.id = move.commercial_partner_id
|
||||
JOIN {currency_table} ON currency_table.company_id = line.company_id
|
||||
'''.format(
|
||||
currency_table=self.env['res.currency']._get_query_currency_table({'multi_company': True, 'date': {'date_to': fields.Date.today()}}),
|
||||
JOIN %(currency_table)s ON account_currency_table.company_id = line.company_id
|
||||
''',
|
||||
currency_table=self.env['res.currency']._get_simple_currency_table(self.env.companies),
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _where(self):
|
||||
return '''
|
||||
def _where(self) -> SQL:
|
||||
return SQL(
|
||||
'''
|
||||
WHERE move.move_type IN ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt')
|
||||
AND line.account_id IS NOT NULL
|
||||
AND line.display_type = 'product'
|
||||
'''
|
||||
''',
|
||||
)
|
||||
|
||||
@api.model
|
||||
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
||||
"""
|
||||
This is a hack to allow us to correctly calculate the average price.
|
||||
"""
|
||||
set_fields = set(fields)
|
||||
|
||||
if 'price_average:avg' in fields:
|
||||
set_fields.add('quantity:sum')
|
||||
set_fields.add('price_subtotal:sum')
|
||||
|
||||
res = super().read_group(domain, list(set_fields), groupby, offset, limit, orderby, lazy)
|
||||
|
||||
if 'price_average:avg' in fields:
|
||||
for data in res:
|
||||
data['price_average'] = data['price_subtotal'] / data['quantity'] if data['quantity'] else 0
|
||||
|
||||
if 'quantity:sum' not in fields:
|
||||
del data['quantity']
|
||||
if 'price_subtotal:sum' not in fields:
|
||||
del data['price_subtotal']
|
||||
|
||||
return res
|
||||
def _read_group_select(self, aggregate_spec: str, query: Query) -> SQL:
|
||||
""" This override allows us to correctly calculate the average price of products. """
|
||||
if aggregate_spec != 'price_average:avg':
|
||||
return super()._read_group_select(aggregate_spec, query)
|
||||
return SQL(
|
||||
'COALESCE(SUM(%(f_price)s) / NULLIF(SUM(%(f_qty)s), 0.0), 0)',
|
||||
f_qty=self._field_to_sql(self._table, 'quantity', query),
|
||||
f_price=self._field_to_sql(self._table, 'price_subtotal', query),
|
||||
)
|
||||
|
||||
|
||||
class ReportInvoiceWithoutPayment(models.AbstractModel):
|
||||
class ReportAccountReport_Invoice(models.AbstractModel):
|
||||
_name = 'report.account.report_invoice'
|
||||
_description = 'Account report without payment lines'
|
||||
|
||||
|
|
@ -180,10 +190,11 @@ class ReportInvoiceWithoutPayment(models.AbstractModel):
|
|||
'qr_code_urls': qr_code_urls,
|
||||
}
|
||||
|
||||
class ReportInvoiceWithPayment(models.AbstractModel):
|
||||
|
||||
class ReportAccountReport_Invoice_With_Payments(models.AbstractModel):
|
||||
_name = 'report.account.report_invoice_with_payments'
|
||||
_description = 'Account report with payment lines'
|
||||
_inherit = 'report.account.report_invoice'
|
||||
_inherit = ['report.account.report_invoice']
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@
|
|||
</record>
|
||||
|
||||
<record id="account_invoice_report_view_tree" model="ir.ui.view">
|
||||
<field name="name">account.invoice.report.view.tree</field>
|
||||
<field name="name">account.invoice.report.view.list</field>
|
||||
<field name="model">account.invoice.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Invoices Analysis">
|
||||
<list string="Invoices Analysis">
|
||||
<field name="move_id" string="Invoice Number"/>
|
||||
<field name="journal_id" optional="hide"/>
|
||||
<field name="partner_id" optional="show"/>
|
||||
|
|
@ -41,12 +41,16 @@
|
|||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="price_average" optional="hide" sum="Total"/>
|
||||
<field name="quantity" optional="hide" sum="Total"/>
|
||||
<field name="price_subtotal_currency" optional="show" sum="Total"/>
|
||||
<field name="price_subtotal" optional="show" sum="Total"/>
|
||||
<field name="price_total" optional="show" sum="Total"/>
|
||||
<field name="price_total_currency" optional="show" sum="Total"/>
|
||||
<field name="price_margin" optional="hide"/>
|
||||
<field name="inventory_value" optional="hide" sum="Total"/>
|
||||
<field name="state" optional="hide"/>
|
||||
<field name="payment_state" optional="hide"/>
|
||||
<field name="move_type" optional="hide"/>
|
||||
</tree>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
@ -55,35 +59,35 @@
|
|||
<field name="name">By Salespersons</field>
|
||||
<field name="model_id">account.invoice.report</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="user_ids" eval="False"/>
|
||||
<field name="context">{'group_by': ['invoice_date:month', 'invoice_user_id']}</field>
|
||||
</record>
|
||||
<record id="filter_invoice_product" model="ir.filters">
|
||||
<field name="name">By Product</field>
|
||||
<field name="model_id">account.invoice.report</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="user_ids" eval="False"/>
|
||||
<field name="context">{'group_by': ['invoice_date:month', 'product_id'], 'set_visible':True, 'residual_invisible':True}</field>
|
||||
</record>
|
||||
<record id="filter_invoice_product_category" model="ir.filters">
|
||||
<field name="name">By Product Category</field>
|
||||
<field name="model_id">account.invoice.report</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="user_ids" eval="False"/>
|
||||
<field name="context">{'group_by': ['invoice_date:month', 'product_categ_id'], 'residual_invisible':True}</field>
|
||||
</record>
|
||||
<record id="filter_invoice_refund" model="ir.filters">
|
||||
<field name="name">By Credit Note</field>
|
||||
<field name="model_id">account.invoice.report</field>
|
||||
<field name="domain">[('move_type', '=', 'out_refund')]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="user_ids" eval="False"/>
|
||||
<field name="context">{'group_by': ['invoice_date:month', 'invoice_user_id']}</field>
|
||||
</record>
|
||||
<record id="filter_invoice_country" model="ir.filters">
|
||||
<field name="name">By Country</field>
|
||||
<field name="model_id">account.invoice.report</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="user_ids" eval="False"/>
|
||||
<field name="context">{'group_by': ['invoice_date:month', 'country_id']}</field>
|
||||
</record>
|
||||
|
||||
|
|
@ -111,7 +115,7 @@
|
|||
<field name="invoice_user_id" />
|
||||
<field name="product_id" />
|
||||
<field name="product_categ_id" filter_domain="[('product_categ_id', 'child_of', self)]"/>
|
||||
<group expand="1" string="Group By">
|
||||
<group>
|
||||
<filter string="Salesperson" name='user' context="{'group_by':'invoice_user_id'}"/>
|
||||
<filter string="Partner" name="partner_id" context="{'group_by':'partner_id','residual_visible':True}"/>
|
||||
<filter string="Product Category" name="category_product" context="{'group_by':'product_categ_id','residual_invisible':True}"/>
|
||||
|
|
@ -129,18 +133,20 @@
|
|||
</record>
|
||||
|
||||
<record id="action_account_invoice_report_all_supp" model="ir.actions.act_window">
|
||||
<field name="name">Invoices Analysis</field>
|
||||
<field name="name">Bills Analysis</field>
|
||||
<field name="res_model">account.invoice.report</field>
|
||||
<field name="path">vendor-bills-analysis</field>
|
||||
<field name="view_mode">graph,pivot</field>
|
||||
<field name="context">{'search_default_current':1, 'search_default_supplier': 1, 'group_by':['invoice_date'], 'group_by_no_leaf':1}</field>
|
||||
<field name="context">{'search_default_current':1, 'search_default_supplier': 1, 'group_by':['invoice_date:month']}</field>
|
||||
<field name="search_view_id" ref="view_account_invoice_report_search"/>
|
||||
<field name="help">From this report, you can have an overview of the amount invoiced from your vendors. The search tool can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
|
||||
</record>
|
||||
<record id="action_account_invoice_report_all" model="ir.actions.act_window">
|
||||
<field name="name">Invoices Analysis</field>
|
||||
<field name="res_model">account.invoice.report</field>
|
||||
<field name="path">customer-invoices-analysis</field>
|
||||
<field name="view_mode">graph,pivot</field>
|
||||
<field name="context">{'search_default_current':1, 'search_default_customer': 1, 'group_by':['invoice_date'], 'group_by_no_leaf':1}</field>
|
||||
<field name="context">{'search_default_current':1, 'search_default_customer': 1, 'group_by':['invoice_date:month']}</field>
|
||||
<field name="search_view_id" ref="view_account_invoice_report_search"/>
|
||||
<field name="help">From this report, you can have an overview of the amount invoiced to your customers. The search tool can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
|
||||
</record>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue