gn_discount and gn_donations in v16
parent
be4786c994
commit
38c9006e82
@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
models/__pycache__
|
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
'name': "Gn Discount",
|
||||
'version': '14.0.0.1.3',
|
||||
'author': 'Garage Numérique',
|
||||
'category': 'Sales',
|
||||
'description': """
|
||||
This module adds the ability to display the total before discount and the discount amount on sales orders and invoices.
|
||||
|
||||
This version only works when no taxes are applied
|
||||
""",
|
||||
'depends': ['sale', 'sale_discount_total'],
|
||||
'data': [
|
||||
'views/sale_view.xml',
|
||||
'views/sale_report.xml',
|
||||
'views/sale_portal.xml',
|
||||
'views/invoice_view.xml',
|
||||
'views/invoice_report.xml'
|
||||
],
|
||||
'translate': True,
|
||||
'translations': [
|
||||
('fr_FR', 'i18n/gn_discount.fr_FR.po'),
|
||||
],
|
||||
'installable': True,
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
msgid "Subtotal"
|
||||
msgstr "Sous-total"
|
||||
|
||||
msgid "Discount"
|
||||
msgtr "Remise"
|
||||
|
||||
msgid "Subtotal with discount"
|
||||
msgtr "Sous-total après remise"
|
||||
|
||||
msgid "Total without discount"
|
||||
msgtr "Total avant remise"
|
||||
|
||||
msgid "Total discounted"
|
||||
msgtr "Total après remise"
|
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import sale, invoice
|
@ -0,0 +1,19 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
amount_undiscounted = fields.Monetary(compute='_compute_amount_undiscounted', store=True)
|
||||
|
||||
@api.depends('amount_untaxed', 'amount_discount')
|
||||
def _compute_amount_undiscounted(self):
|
||||
for record in self:
|
||||
record.amount_undiscounted = record.amount_untaxed + record.amount_discount
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = 'account.move.line'
|
||||
price_subtotal_before_discount = fields.Monetary(compute='_compute_amount_undiscounted', store=True, string="Subtotal", translate="True")
|
||||
|
||||
@api.depends('price_unit', 'quantity')
|
||||
def _compute_amount_undiscounted(self):
|
||||
for record in self:
|
||||
record.price_subtotal_before_discount = record.price_unit * record.quantity
|
@ -0,0 +1,11 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
price_subtotal_before_discount = fields.Monetary(readonly=True, string="Subtotal", translate=True, compute='_compute_price_subtotal_before_discount')
|
||||
|
||||
@api.depends('price_unit', 'discount', 'product_uom_qty')
|
||||
def _compute_price_subtotal_before_discount(self):
|
||||
for line in self:
|
||||
line.price_subtotal_before_discount = line.price_unit * line.product_uom_qty
|
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="gn_discount_report_invoice" inherit_id="account.report_invoice_document">
|
||||
<!--<xpath expr="//span[@t-field='o.amount_untaxed']" position="replace">
|
||||
<span t-field="o.amount_undiscounted"/>
|
||||
|
||||
</xpath>-->
|
||||
|
||||
|
||||
<!--<xpath expr="//tr[td[strong[text()='Subtotal']]]" position="replace">-->
|
||||
<xpath expr="//t[@t-set='display_discount']" position="replace">
|
||||
<t t-set="display_discount" t-value="false"/>
|
||||
</xpath>
|
||||
<xpath expr="//tr[@class='border-black o_subtotal']" position="replace">
|
||||
<t t-if="o.amount_discount > 0">
|
||||
<tr class="border-black">
|
||||
<td><strong>Subtotal</strong></td>
|
||||
<td class="text-right">
|
||||
<span
|
||||
t-if="o.amount_discount >= 0"
|
||||
t-att-style="'text-decoration: line-through' or None"
|
||||
t-att-class="'text-danger' or ''"
|
||||
data-id="total_before_discount"
|
||||
t-field="o.amount_undiscounted"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Discount**</td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<span>-</span>
|
||||
<span t-if="o.amount_discount" t-field="o.amount_discount"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Subtotal with discount</strong></td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<div t-if="o.amount_discount" t-field="o.amount_untaxed">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
<t-else>
|
||||
<tr class="border-black">
|
||||
<td><strong>Subtotal</strong></td>
|
||||
<td class="text-right">
|
||||
<span t-field="o.amount_untaxed"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t-else>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//td[.//span[@t-field='line.price_subtotal']]" position="replace">
|
||||
<td class="text-right o_price_total">
|
||||
<span t-field="line.price_subtotal_before_discount"/>
|
||||
</td>
|
||||
</xpath>
|
||||
|
||||
</template>
|
||||
</odoo>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="gn_discount_invoice_view_form" model="ir.ui.view">
|
||||
<field name="name">gn_discount.account.move.form</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='amount_untaxed']" position="replace">
|
||||
<field name="amount_undiscounted" widget="monetary"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='amount_by_group']" position="before">
|
||||
<field name="amount_untaxed"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='price_subtotal']" position="replace">
|
||||
<field name="price_subtotal_before_discount" readonly="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="gn_discount.portal_order_view_template" inherit_id="sale.sale_order_portal_content">
|
||||
<xpath expr="//td[.//span[@class='oe_order_line_price_subtotal']]" position="replace">
|
||||
<td class="text-right">
|
||||
<span class="oe_order_line_price_subtotal" t-field="line.price_subtotal_before_discount"/>
|
||||
</td>
|
||||
</xpath>
|
||||
<xpath expr="//td[div[@t-field='line.price_unit']]" position="replace">
|
||||
<td t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">
|
||||
<div t-field="line.price_unit" t-attf-class="text-right"/>
|
||||
</td>
|
||||
</xpath>
|
||||
<xpath expr="//t[@t-set='display_discount']" position="replace">
|
||||
<t t-set="display_discount" t-value="false"/>
|
||||
</xpath>
|
||||
<!--<xpath expr="//strong[@class='text-info']" position="replace">
|
||||
<strong t-if="line.discount > 0" class="text-info">
|
||||
<t t-esc="'{:.2f}'.format(line.discount)"/>%
|
||||
</strong>
|
||||
</xpath>-->
|
||||
</template>
|
||||
|
||||
<template id="gn_discount.portal_order_view_totals" inherit_id="sale.sale_order_portal_content_totals_table">
|
||||
<xpath expr="//tr[@class='border-black'][1]" position="replace">
|
||||
<t t-if="sale_order.amount_discount > 0">
|
||||
<tr class="border-black">
|
||||
<td><strong>Subtotal</strong></td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<span
|
||||
t-att-style="'text-decoration: line-through' or None"
|
||||
t-att-class="'text-danger' or ''"
|
||||
data-id="total_before_discount"
|
||||
t-field="sale_order.amount_undiscounted"
|
||||
t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Discount</td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<div t-if="sale_order.amount_discount">
|
||||
<span>-</span><t t-esc="sale_order.amount_discount" t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Subtotal with discount</strong></td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<div t-if="sale_order.amount_discount">
|
||||
<t t-esc="sale_order.amount_untaxed" t-options='{"widget": "monetary","display_currency": sale_order.pricelist_id.currency_id}'/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
<t-else>
|
||||
<tr class="border-black">
|
||||
<td><strong>Subtotal</strong></td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<span t-field="sale_order.amount_untaxed"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</t-else>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
</odoo>
|
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="gn_discount_report_saleorder_document" inherit_id="sale.report_saleorder_document">
|
||||
<xpath expr="//tr[@class='border-black o_subtotal']" position="replace">
|
||||
<t t-if="doc.amount_discount > 0">
|
||||
<tr class="border-black">
|
||||
<td><strong>Subtotal</strong></td>
|
||||
<td class="text-right">
|
||||
<span
|
||||
t-att-style="'text-decoration: line-through' or None"
|
||||
t-att-class="'text-danger' or ''"
|
||||
data-id="total_before_discount"
|
||||
t-field="doc.amount_undiscounted"
|
||||
t-options='{"widget": "monetary","display_currency": doc.currency_id}'
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Discount**</td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<span>-</span>
|
||||
<span t-if="doc.amount_discount" t-field="doc.amount_discount"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Subtotal with discount</strong></td>
|
||||
<td class="text-right">
|
||||
<div>
|
||||
<div t-if="doc.amount_discount" t-field="doc.amount_untaxed">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
<t-else>
|
||||
<tr class="border-black">
|
||||
<td><strong>Subtotal</strong></td>
|
||||
<td class="text-right">
|
||||
<span t-field="doc.amount_untaxed"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t-else>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//td[@name='td_subtotal']" position="replace">
|
||||
<td name="td_subtotal_before_discount" class="text-right">
|
||||
<span t-field="line.price_subtotal_before_discount" t-options="{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}"/>
|
||||
</td>
|
||||
</xpath>
|
||||
|
||||
|
||||
</template>
|
||||
</odoo>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="gn_discount_sale_view_form" model="ir.ui.view">
|
||||
<field name="name">gn_discount.sale.order.form</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="priority">99</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='amount_untaxed']" position="replace">
|
||||
<field name="amount_undiscounted" widget="monetary" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='amount_tax']" position="before">
|
||||
<field name="amount_untaxed" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="replace">
|
||||
<field name="price_subtotal_before_discount" readonly="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
models/__pycache__
|
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
'name': "Gn Donations",
|
||||
'version': '14.0.0.1.1',
|
||||
'author': 'Garage Numérique',
|
||||
'category': 'Accounting',
|
||||
'description': """
|
||||
This module modify the fiscal receipt report so it is similar to Cerfa 11580.
|
||||
It allows to edit fiscal receipts for in-kind donation (i.e. 0€ donation).
|
||||
""",
|
||||
'depends': ['donation'],
|
||||
'data': [
|
||||
'views/donation_thanks_report.xml',
|
||||
'views/internal_layout.xml'
|
||||
],
|
||||
'translate': True,
|
||||
'installable': True,
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import donation
|
@ -0,0 +1,127 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import fields, models
|
||||
import num2words
|
||||
|
||||
class TaxReceipt(models.Model):
|
||||
_inherit = 'donation.tax.receipt'
|
||||
amount_in_words = fields.Char(compute='_compute_amount_in_words', string='Amount in Words')
|
||||
|
||||
def _compute_amount_in_words(self):
|
||||
for record in self:
|
||||
record.amount_in_words = num2words.num2words(record.amount, lang='fr')
|
||||
|
||||
|
||||
from odoo import _, api
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools.misc import format_amount
|
||||
|
||||
from odoo.addons.account import _auto_install_l10n
|
||||
# Rewriting donation to allow 0€ fiscal receipts
|
||||
class DonationDonation(models.Model):
|
||||
_inherit = 'donation.donation'
|
||||
|
||||
def validate(self):
|
||||
check_total = self.env["res.users"].has_group(
|
||||
"donation.group_donation_check_total"
|
||||
)
|
||||
for donation in self:
|
||||
if donation.donation_date > fields.Date.context_today(self):
|
||||
raise UserError(
|
||||
_(
|
||||
"The date of donation %s should be today "
|
||||
"or in the past, not in the future!"
|
||||
)
|
||||
% donation.number
|
||||
)
|
||||
if not donation.line_ids:
|
||||
raise UserError(
|
||||
_(
|
||||
"Cannot validate donation %s because it doesn't "
|
||||
"have any lines!"
|
||||
)
|
||||
% donation.number
|
||||
)
|
||||
|
||||
'''
|
||||
# The part we don't want, in order ta validate 0€ fiscal receipts
|
||||
if donation.currency_id.is_zero(donation.amount_total):
|
||||
raise UserError(
|
||||
_("Cannot validate donation %s because the " "total amount is 0!")
|
||||
% donation.number
|
||||
)
|
||||
'''
|
||||
|
||||
if donation.state != "draft":
|
||||
raise UserError(
|
||||
_(
|
||||
"Cannot validate donation %s because it is not "
|
||||
"in draft state."
|
||||
)
|
||||
% donation.number
|
||||
)
|
||||
|
||||
if check_total and donation.currency_id.compare_amounts(
|
||||
donation.check_total, donation.amount_total
|
||||
):
|
||||
raise UserError(
|
||||
_(
|
||||
"The amount of donation %s (%s) is different "
|
||||
"from the sum of the donation lines (%s)."
|
||||
)
|
||||
% (
|
||||
donation.number,
|
||||
format_amount(
|
||||
self.env, donation.check_total, donation.currency_id
|
||||
),
|
||||
format_amount(
|
||||
self.env, donation.amount_total, donation.currency_id
|
||||
),
|
||||
)
|
||||
)
|
||||
full_in_kind = all([line.in_kind for line in donation.line_ids])
|
||||
if not donation.payment_mode_id and not full_in_kind:
|
||||
raise UserError(
|
||||
_(
|
||||
"Payment Mode is not set on donation %s (only fully "
|
||||
"in-kind donations don't require a payment mode)."
|
||||
)
|
||||
% donation.number
|
||||
)
|
||||
|
||||
vals = {"state": "done"}
|
||||
if full_in_kind and donation.payment_mode_id:
|
||||
vals["payment_mode_id"] = False
|
||||
|
||||
if not full_in_kind:
|
||||
move_vals = donation._prepare_donation_move()
|
||||
# when we have a full in-kind donation: no account move
|
||||
if move_vals:
|
||||
move = self.env["account.move"].create(move_vals)
|
||||
move.action_post()
|
||||
vals["move_id"] = move.id
|
||||
else:
|
||||
donation.message_post(
|
||||
body=_("Full in-kind donation: no account move generated")
|
||||
)
|
||||
|
||||
receipt = donation.generate_each_tax_receipt()
|
||||
if receipt:
|
||||
vals["tax_receipt_id"] = receipt.id
|
||||
|
||||
donation.write(vals)
|
||||
if donation.bank_statement_line_id:
|
||||
donation._reconcile_donation_from_bank_statement()
|
||||
donation.partner_id._update_donor_rank()
|
||||
return
|
||||
|
||||
def generate_each_tax_receipt(self):
|
||||
self.ensure_one()
|
||||
receipt = False
|
||||
if (
|
||||
self.tax_receipt_option == "each"
|
||||
and not self.tax_receipt_id
|
||||
#and not self.company_currency_id.is_zero(self.tax_receipt_total)
|
||||
):
|
||||
receipt_vals = self._prepare_each_tax_receipt()
|
||||
receipt = self.env["donation.tax.receipt"].create(receipt_vals)
|
||||
return receipt
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<template id="gn_donations.internal_layout">
|
||||
<div class="header">
|
||||
</div>
|
||||
|
||||
<div class="article" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id" t-att-data-oe-lang="o and o.env.context.get('lang')">
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
</template>
|
||||
</odoo>
|
Loading…
Reference in New Issue