diff --git a/donation_base/README.rst b/donation_base/README.rst new file mode 100644 index 000000000..5ad67e89c --- /dev/null +++ b/donation_base/README.rst @@ -0,0 +1,51 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============= +Donation Base +============= + +This is the base module for donations. This module doesn't do anything in itself ; it just adds some properties on products and partners and adds the *donation.tax.receipt* object. + +To get some real features, you should install the *donation* or the *donation_sale* module. To understand the difference between these 2 modules, read `this post `_. + +Configuration +============= + +To configure this module, you need to: + + * create donation products + * set the *Tax Receipt Option* on partners + +Usage +===== + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/180/9.0 + +Credits +======= + +Contributors +------------ + +* Brother Bernard +* Brother Irénée (Barroux Abbey) +* Alexis de Lattre + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/donation_base/__init__.py b/donation_base/__init__.py new file mode 100644 index 000000000..35e7c9600 --- /dev/null +++ b/donation_base/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import wizard diff --git a/donation_base/__openerp__.py b/donation_base/__openerp__.py new file mode 100644 index 000000000..968b2ba34 --- /dev/null +++ b/donation_base/__openerp__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# © 2014-2016 Barroux Abbey (http://www.barroux.org) +# © 2014-2016 Akretion France (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Donation Base', + 'version': '9.0.1.0.0', + 'category': 'Accounting & Finance', + 'license': 'AGPL-3', + 'summary': 'Base module for donations', + 'author': 'Barroux Abbey, Akretion, Odoo Community Association (OCA)', + 'website': 'http://www.barroux.org', + 'depends': ['account'], + 'data': [ + 'security/ir.model.access.csv', + 'security/tax_receipt_security.xml', + 'data/donation_tax_seq.xml', + 'views/product.xml', + 'views/partner.xml', + 'views/donation_tax_receipt.xml', + 'report/report.xml', + 'report/report_donationtax.xml', + 'wizard/tax_receipt_annual_create_view.xml', + 'wizard/tax_receipt_print_view.xml', + ], + 'demo': ['demo/donation_demo.xml'], + 'installable': True, +} diff --git a/donation_base/data/donation_tax_seq.xml b/donation_base/data/donation_tax_seq.xml new file mode 100644 index 000000000..3b19ba449 --- /dev/null +++ b/donation_base/data/donation_tax_seq.xml @@ -0,0 +1,21 @@ + + + + + + + + Donation Tax Receipt + donation.tax.receipt + + %(year)s- + 5 + + + + + diff --git a/donation_base/demo/donation_demo.xml b/donation_base/demo/donation_demo.xml new file mode 100644 index 000000000..07c540887 --- /dev/null +++ b/donation_base/demo/donation_demo.xml @@ -0,0 +1,104 @@ + + + + + + + + + Donation + DON + + + + + 0 + service + + + This donation item is eligible for a tax receipt. + + + + + Donation - no tax receipt + DON-NOTAXR + + + + + 0 + service + + + This donation item is not eligible for a tax receipt. + + + + In-Kind Donation + KIND-DON + + + + + + 0 + service + + + This donation item is eligible for a tax receipt. + + + + In-Kind Donation - no tax receipt + KIND-DON-NOTAXR + + + + + + 0 + service + + + This donation item is not eligible for a tax receipt. + + + + + + Rémi Duplat + + 12 rue de l'espérance + 69100 + Villeurbanne + + vincent.duplat@yahoo.example.com + each + + + + Lucie Dubois + + 34 rue Pierre Dupont + 69001 + Lyon + + lucie.dubois@yahoo.example.com + annual + + + + Joe Smith + + Craig Pond Trail + 04431 + East Orland + + + joe.smith@gmail.example.com + none + + + + diff --git a/donation_base/models/__init__.py b/donation_base/models/__init__.py new file mode 100644 index 000000000..53867e6ef --- /dev/null +++ b/donation_base/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import product +from . import partner +from . import donation_tax_receipt diff --git a/donation_base/models/donation_tax_receipt.py b/donation_base/models/donation_tax_receipt.py new file mode 100644 index 000000000..e06ed1026 --- /dev/null +++ b/donation_base/models/donation_tax_receipt.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# © 2014-2016 Barroux Abbey (http://www.barroux.org) +# © 2014-2016 Akretion France (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api +import openerp.addons.decimal_precision as dp + + +class DonationTaxReceipt(models.Model): + _name = 'donation.tax.receipt' + _description = "Tax Receipt for Donations" + _order = 'id desc' + _rec_name = 'number' + + number = fields.Char(string='Receipt Number') + date = fields.Date( + string='Date', required=True, default=fields.Date.context_today) + donation_date = fields.Date(string='Donation Date') + amount = fields.Monetary( + string='Amount', digits=dp.get_precision('Account'), + currency_field='currency_id') + currency_id = fields.Many2one( + 'res.currency', string='Currency', required=True, ondelete='restrict') + partner_id = fields.Many2one( + 'res.partner', string='Donor', required=True, ondelete='restrict', + domain=[('parent_id', '=', False)]) + company_id = fields.Many2one( + 'res.company', string='Company', required=True, + default=lambda self: self.env['res.company']._company_default_get( + 'donation.tax.receipt')) + print_date = fields.Date(string='Print Date') + type = fields.Selection([ + ('each', 'One-Time Tax Receipt'), + ('annual', 'Annual Tax Receipt'), + ], string='Type', required=True) + + # Maybe we can drop that code with the new seq management on v9 + @api.model + def create(self, vals=None): + if vals is None: + vals = {} + date = vals.get('donation_date') + vals['number'] = self.env['ir.sequence'].with_context( + date=date).next_by_code('donation.tax.receipt') + return super(DonationTaxReceipt, self).create(vals) diff --git a/donation_base/models/partner.py b/donation_base/models/partner.py new file mode 100644 index 000000000..0be33cee0 --- /dev/null +++ b/donation_base/models/partner.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# © 2014-2016 Barroux Abbey (http://www.barroux.org) +# © 2014-2016 Akretion France (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + tax_receipt_option = fields.Selection([ + ('none', 'None'), + ('each', 'For Each Donation'), + ('annual', 'Annual Tax Receipt'), + ], string='Tax Receipt Option', track_visibility='onchange') + + @api.model + def _commercial_fields(self): + res = super(ResPartner, self)._commercial_fields() + res.append('tax_receipt_option') + return res diff --git a/donation_base/models/product.py b/donation_base/models/product.py new file mode 100644 index 000000000..5b228fb95 --- /dev/null +++ b/donation_base/models/product.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# © 2014-2016 Barroux Abbey (http://www.barroux.org) +# © 2014-2016 Akretion France (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ +from openerp.exceptions import ValidationError + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + donation = fields.Boolean( + string='Is a Donation', track_visibility='onchange', + help="Specify if the product can be selected in a donation line.") + in_kind_donation = fields.Boolean( + string="In-Kind Donation", track_visibility='onchange') + tax_receipt_ok = fields.Boolean( + string='Is Eligible for a Tax Receipt', track_visibility='onchange', + help="Specify if the product is eligible for a tax receipt") + + @api.onchange('donation') + def _donation_change(self): + if self.donation: + self.type = 'service' + + @api.onchange('in_kind_donation') + def _in_kind_donation_change(self): + if self.in_kind_donation: + self.donation = True + + @api.multi + @api.constrains('donation', 'type') + def donation_check(self): + for product in self: + if product.donation and product.type != 'service': + raise ValidationError(_( + "The product '%s' is a donation, so you must " + "configure it as a Service") % product.name) + if product.in_kind_donation and not product.donation: + raise ValidationError(_( + "The option 'In-Kind Donation' is active on " + "the product '%s', so you must also activate the " + "option 'Is a Donation'.") % product.name) + if product.tax_receipt_ok and not product.donation: + raise ValidationError(_( + "The option 'Is Eligible for a Tax Receipt' is " + "active on the product '%s', so you must also activate " + "the option 'Is a Donation'.") % product.name) + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + @api.onchange('donation') + def _donation_change(self): + if self.donation: + self.type = 'service' + + @api.onchange('in_kind_donation') + def _in_kind_donation_change(self): + if self.in_kind_donation: + self.donation = True diff --git a/donation_base/report/report.xml b/donation_base/report/report.xml new file mode 100644 index 000000000..864556de1 --- /dev/null +++ b/donation_base/report/report.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/donation_base/report/report_donationtax.xml b/donation_base/report/report_donationtax.xml new file mode 100644 index 000000000..1026a60bf --- /dev/null +++ b/donation_base/report/report_donationtax.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/donation_base/security/ir.model.access.csv b/donation_base/security/ir.model.access.csv new file mode 100644 index 000000000..b453effab --- /dev/null +++ b/donation_base/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_donation_tax_receipt_full,Full access on donation.tax.receipt to Config grp,model_donation_tax_receipt,base.group_system,1,1,1,1 diff --git a/donation_base/security/tax_receipt_security.xml b/donation_base/security/tax_receipt_security.xml new file mode 100644 index 000000000..f96a11075 --- /dev/null +++ b/donation_base/security/tax_receipt_security.xml @@ -0,0 +1,18 @@ + + + + + + + + Donation Tax Receipt Multi-company + + ['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])] + + + + diff --git a/donation_base/views/donation_tax_receipt.xml b/donation_base/views/donation_tax_receipt.xml new file mode 100644 index 000000000..b944be75e --- /dev/null +++ b/donation_base/views/donation_tax_receipt.xml @@ -0,0 +1,104 @@ + + + + + + + + + donation.tax.receipt.form + donation.tax.receipt + +
+ + + + + + + + + + + +
+
+
+ + + + donation.tax.receipt.tree + donation.tax.receipt + + + + + + + + + + + + + + + + + donation.tax.receipt.search + donation.tax.receipt + + + + + + + + + + + + + + + + + donation.tax.receipt.graph + donation.tax.receipt + + + + + + + + + + donation.tax.receipt.pivot + donation.tax.receipt + + + + + + + + + + + Donation Tax Receipt + donation.tax.receipt + tree,form,graph,pivot + + + +
+
diff --git a/donation_base/views/partner.xml b/donation_base/views/partner.xml new file mode 100644 index 000000000..2adafb786 --- /dev/null +++ b/donation_base/views/partner.xml @@ -0,0 +1,24 @@ + + + + + + + + donation.tax.receipt.res.partner.form + res.partner + + + + + + + + + + diff --git a/donation_base/views/product.xml b/donation_base/views/product.xml new file mode 100644 index 000000000..88f6d625e --- /dev/null +++ b/donation_base/views/product.xml @@ -0,0 +1,52 @@ + + + + + + + + donation.product.template.search + product.template + + + + + + + + + + + donation.product.template.form + product.template + + +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
diff --git a/donation_base/wizard/__init__.py b/donation_base/wizard/__init__.py new file mode 100644 index 000000000..8cd4b9675 --- /dev/null +++ b/donation_base/wizard/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import tax_receipt_print +from . import tax_receipt_annual_create diff --git a/donation_base/wizard/tax_receipt_annual_create.py b/donation_base/wizard/tax_receipt_annual_create.py new file mode 100644 index 000000000..d7ca0f743 --- /dev/null +++ b/donation_base/wizard/tax_receipt_annual_create.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# © 2014-2016 Barroux Abbey (http://www.barroux.org) +# © 2014-2016 Akretion France (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ +from openerp.exceptions import UserError +from datetime import datetime + + +class TaxReceiptAnnualCreate(models.TransientModel): + _name = 'tax.receipt.annual.create' + _description = 'Generate Annual Tax Receipt' + + @api.model + def _default_end_date(self): + return datetime(datetime.today().year - 1, 12, 31) + + @api.model + def _default_start_date(self): + return datetime(datetime.today().year - 1, 1, 1) + + start_date = fields.Date( + 'Start Date', required=True, default=_default_start_date) + end_date = fields.Date( + 'End Date', required=True, default=_default_end_date) + + # TODO: adapt code to make it independant of the donation module + @api.model + def _prepare_annual_tax_receipt(self, partner_id, partner_dict): + vals = { + 'company_id': self.env.user.company_id.id, + 'currency_id': self.env.user.company_id.currency_id.id, + 'amount': partner_dict['amount'], + 'type': 'annual', + 'partner_id': partner_id, + 'date': self.end_date, + 'donation_date': self.end_date, + 'donation_ids': [(6, 0, partner_dict['donation_ids'])], + } + return vals + + @api.multi + def generate_annual_receipts(self): + self.ensure_one() + donations = self.env['donation.donation'].search([ + ('donation_date', '>=', self.start_date), + ('donation_date', '<=', self.end_date), + ('tax_receipt_option', '=', 'annual'), + ('tax_receipt_id', '=', False), + ('tax_receipt_total', '!=', 0), + ('company_id', '=', self.env.user.company_id.id), + ('state', '=', 'done'), + ]) + tax_receipt_annual = {} + # {partner_id: { + # 'amount': amount, + # 'donation_ids': [donation1_id, donation2_id]}} + for donation in donations: + partner_id = donation.commercial_partner_id.id + tax_receipt_amount = donation.tax_receipt_total + if partner_id not in tax_receipt_annual: + tax_receipt_annual[partner_id] = { + 'amount': tax_receipt_amount, + 'donation_ids': [donation.id], + } + else: + tax_receipt_annual[partner_id]['amount'] +=\ + tax_receipt_amount + tax_receipt_annual[partner_id]['donation_ids']\ + .append(donation.id) + + tax_receipt_ids = [] + for partner_id, partner_dict in tax_receipt_annual.iteritems(): + vals = self._prepare_annual_tax_receipt(partner_id, partner_dict) + # Block if the partner already has an annual fiscal receipt + # or an each fiscal receipt + already_tax_receipts = \ + self.env['donation.tax.receipt'].search([ + ('date', '<=', self.end_date), + ('date', '>=', self.start_date), + ('company_id', '=', vals['company_id']), + ('partner_id', '=', vals['partner_id']), + ]) + if already_tax_receipts: + partner = self.env['res.partner'].browse(vals['partner_id']) + raise UserError( + _("The Donor '%s' already has a tax receipt " + "in this timeframe: %s dated %s.") + % (partner.name, already_tax_receipts[0].number, + already_tax_receipts[0].date)) + tax_receipt = self.env['donation.tax.receipt'].create(vals) + tax_receipt_ids.append(tax_receipt.id) + action = { + 'type': 'ir.actions.act_window', + 'name': 'Tax Receipts', + 'res_model': 'donation.tax.receipt', + 'view_mode': 'tree,form,graph', + 'nodestroy': False, + 'target': 'current', + 'domain': [('id', 'in', tax_receipt_ids)], + } + return action diff --git a/donation_base/wizard/tax_receipt_annual_create_view.xml b/donation_base/wizard/tax_receipt_annual_create_view.xml new file mode 100644 index 000000000..ce3806977 --- /dev/null +++ b/donation_base/wizard/tax_receipt_annual_create_view.xml @@ -0,0 +1,39 @@ + + + + + + + + + tax_receipt_annual_create.form + tax.receipt.annual.create + +
+ + + + +
+
+
+
+
+ + + Create Annual Receipts + tax.receipt.annual.create + form + new + + + +
+
diff --git a/donation_base/wizard/tax_receipt_print.py b/donation_base/wizard/tax_receipt_print.py new file mode 100644 index 000000000..317989d7b --- /dev/null +++ b/donation_base/wizard/tax_receipt_print.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# © 2014-2016 Barroux Abbey (http://www.barroux.org) +# © 2014-2016 Akretion France (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ +from openerp.exceptions import UserError + + +class DonationTaxReceiptPrint(models.TransientModel): + _name = 'donation.tax.receipt.print' + _description = 'Print Donation Tax Receipt' + + @api.model + def _get_receipts(self): + return self.env['donation.tax.receipt'].search( + [('print_date', '=', False)]) + + receipt_ids = fields.Many2many( + 'donation.tax.receipt', + column1='print_wizard_id', column2='receipt_id', + string='Receipts To Print', default=_get_receipts) + + @api.multi + def print_receipts(self): + self.ensure_one() + if not self.receipt_ids: + raise UserError( + _('There are no tax receipts to print.')) + datas = { + 'model': 'donation.tax.receipt', + 'ids': self.receipt_ids.ids, + } + today = fields.Date.context_today(self) + self.receipt_ids.write({'print_date': today}) + action = { + 'type': 'ir.actions.report.xml', + 'report_name': 'donation_tax_receipt.report_donationtaxreceipt', + 'data': datas, + 'datas': datas, # for Aeroo + } + return action diff --git a/donation_base/wizard/tax_receipt_print_view.xml b/donation_base/wizard/tax_receipt_print_view.xml new file mode 100644 index 000000000..314d6cfa0 --- /dev/null +++ b/donation_base/wizard/tax_receipt_print_view.xml @@ -0,0 +1,38 @@ + + + + + + + + + donation_tax_receipt_print.form + donation.tax.receipt.print + +
+ + + +
+
+
+
+
+ + + Print Receipts + donation.tax.receipt.print + form + new + + + +
+