diff --git a/__init__.py b/__init__.py index e8bfb5afadcebdbc168096b442d8e2e5347d8c72..d0cc7ea8a81fcaff98026156d5308fcd32dfe192 100644 --- a/__init__.py +++ b/__init__.py @@ -3,3 +3,4 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import models +from . import wizard diff --git a/__manifest__.py b/__manifest__.py index 28cf9481f6dda64825ca52aacead27fddf4c7ca6..73a282889f55d9c8a3305e88017bb5260466bcbc 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -17,6 +17,7 @@ 'views/account_invoice_pcafae_line.xml', 'views/sale_order.xml', 'views/sale_order_pcafae_line.xml', - 'views/menus.xml' + 'views/menus.xml', + 'wizard/pca_fae_export_wizard.xml', ], } diff --git a/models/account_invoice.py b/models/account_invoice.py index 47bffa3364c08d011330f1ac634a2abe9f70e0ee..ca80621d1e0ce31399e478dcd91f47626dd6ccf4 100644 --- a/models/account_invoice.py +++ b/models/account_invoice.py @@ -28,6 +28,8 @@ class AccountInvoicePcaFae(models.Model): store=True, default=False ) + pca_is_exported = fields.Boolean("PCA exporté ?", default=False) + pca_export_date = fields.Datetime("Date export PCA") # ------------------------------------------------------ # Constrains function @@ -97,4 +99,4 @@ class AccountInvoicePcaFae(models.Model): # si PCA, on affecte le PCA négatif sur l'année en cours elif line.pca_last_year: line_fy_id.pca = -line.pca_last_year - return res \ No newline at end of file + return res diff --git a/views/account_invoice.xml b/views/account_invoice.xml index 625f26eff389030a2300193855ac03435afab767..29a826c3f9ec09463c682327d9da2e428ef3762c 100644 --- a/views/account_invoice.xml +++ b/views/account_invoice.xml @@ -13,8 +13,12 @@ <group> <field name="fiscal_year" readonly="1"/> </group> + <group> + <field name="pca_is_exported" readonly="1"/> + <field name="pca_export_date" readonly="1"/> + </group> </group> - <field name="invoice_pcafae_line_ids"> + <field name="invoice_pcafae_line_ids" attrs="{'readonly': [('pca_export_date', '=', True)]}"> <tree editable="bottom"> <field name="year"/> <field name="year_percent"/> @@ -46,6 +50,9 @@ <filter name="late" position="after"> <separator/> <filter string="Non ventilé" name="is_weighted" domain="[('is_weighted','=',False)]"/> + <separator/> + <filter string="PCA exporté" name="pca_is_exported" domain="[('pca_is_exported','=',True)]"/> + <filter string="PCA non exporté" name="pca_is_not_exported" domain="[('is_weighted','=',False)]"/> </filter> </field> </record> diff --git a/wizard/__init__.py b/wizard/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b83d5bae3c64166242efd66824be9619196c610b --- /dev/null +++ b/wizard/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2020 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import pca_fae_export_wizard diff --git a/wizard/pca_fae_export_wizard.py b/wizard/pca_fae_export_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..29e112f93ec6137948068634b97669f022973864 --- /dev/null +++ b/wizard/pca_fae_export_wizard.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019 Le Filament (<http://www.le-filament.com>) +# License GPL-3 or later (http://www.gnu.org/licenses/gpl.html). + +from datetime import datetime +import csv +import re +from cStringIO import StringIO + +from odoo import models, fields, api, http +from odoo.addons.web.controllers.main import serialize_exception +from odoo.addons.web.controllers.main import content_disposition +from odoo.http import request +from odoo.tools.misc import xlwt + +HEADER_DEFAULT = [ + 'date', + 'journal', + 'compte', + 'debit', + 'credit', + 'libelle', + 'piece', + 'echeance', + 'ref_piece' + ] + + +class PCAExportWizard(models.TransientModel): + _name = "pca.export.wizard" + _description = "Export des lignes PCA" + + date_start = fields.Date('Date de début', required=True) + date_end = fields.Date( + string='Date de fin', + required=True, + default=datetime.today().strftime('%Y-%m-%d') + ) + export_format = fields.Selection( + [('csv', 'CSV'), ('xls', 'Excel')], + string='Format', default="csv") + export_type = fields.Selection( + [('empty', 'Factures non exportées'), ('all', 'Toutes les factures')], + string='Factures à exporter', default="empty" + ) + + @api.multi + def get_data_file(self): + return { + 'type': 'ir.actions.act_url', + 'url': '/web/export_pca?format=%s&type=%s' % + (self.export_format, self.export_type) + + '&date_start=%s&date_end=%s' % + (self.date_start, self.date_end), + 'target': 'new', + } + + +class PCAExportController(http.Controller): + def export_csv(self, lignes_export, header, filename_): + fp = StringIO() + + export_file = csv.writer(fp, delimiter=';', quoting=csv.QUOTE_ALL) + # export_file.writerow(header) + + row = [] + for line in lignes_export: + row = [] + for h in header: + if isinstance(line[h], unicode): + try: + value = line[h].encode('utf-8') + except UnicodeError: + pass + else: + if (h == 'date') or (h == 'echeance'): + if line[h]: + value = datetime.strptime(line[h], '%Y-%m-%d')\ + .strftime('%d/%m/%Y') + else: + value = line[h] + elif (h == 'credit') or (h == 'debit'): + value = str(line[h]).replace('.', ',') + else: + value = line[h] + row.append(value) + + export_file.writerow(row) + + fp.seek(0) + data = fp.read() + fp.close() + + filename = filename_ + '.csv' + + csvhttpheaders = [ + ('Content-Type', 'text/csv;charset=utf8'), + ('Content-Disposition', content_disposition(filename)), + ] + + return request.make_response(data, headers=csvhttpheaders) + + def export_xls(self, lignes_export, header, filename_): + workbook = xlwt.Workbook() + worksheet = workbook.add_sheet(filename_) + + header_file = header + + for i, fieldname in enumerate(header_file): + worksheet.write(0, i, fieldname) + worksheet.col(i).width = 8000 # around 220 pixels + + base_style = xlwt.easyxf('align: wrap yes') + + for row_index, line in enumerate(lignes_export): + for cell_index, h in enumerate(header): + cell_style = base_style + if (h == 'date') or (h == 'echeance'): + if line[h]: + cell_value = datetime.strptime(line[h], '%Y-%m-%d')\ + .strftime('%d/%m/%Y') + else: + cell_value = line[h] + elif (h == 'credit') or (h == 'debit'): + cell_value = line[h] + elif isinstance(line[h], basestring): + cell_value = re.sub("\r", " ", line[h]) + else: + cell_value = line[h] + worksheet.write(row_index + 1, cell_index, + cell_value, cell_style) + + fp = StringIO() + workbook.save(fp) + fp.seek(0) + data = fp.read() + fp.close() + + filename = filename_ + '.xls' + + csvhttpheaders = [ + ('Content-Type', 'text/csv;charset=utf8'), + ('Content-Disposition', content_disposition(filename)), + ] + + return request.make_response(data, headers=csvhttpheaders) + + def datas_export_ventes(self, format, type, date_start, date_end,): + header = HEADER_DEFAULT + lignes_export = [] + + # requete + domain = [ + ('date_invoice', '>=', date_start), + ('date_invoice', '<=', date_end), + ('state', 'in', ['open', 'paid']), + ] + if type == 'empty': + domain += [('pca_is_exported', '!=', True)] + + invoice_ids = request.env['account.invoice'].search(domain) + for invoice in invoice_ids: + for line in invoice.invoice_line_ids: + for pca in invoice.invoice_pcafae_line_ids: + if pca.year > invoice.fiscal_year: + lignes_export.append({ + 'date': invoice.date_invoice, + 'journal': 'OD', + 'compte': line.account_id.code, + 'debit': line.price_subtotal_signed * pca.year_percent * 0.01, + 'credit': 0.0, + 'libelle': invoice.partner_id.name + ' - Facture ' + invoice.number + ' - ' + line.name, + 'piece': invoice.number, + 'echeance': invoice.date_due, + 'ref_piece': invoice.number + }) + lignes_export.append({ + 'date': invoice.date_invoice, + 'journal': 'OD', + 'compte': '487000', + 'debit': 0.0, + 'credit': line.price_subtotal_signed * pca.year_percent * 0.01, + 'libelle': invoice.partner_id.name + ' - Facture ' + invoice.number + ' - ' + line.name, + 'piece': invoice.number, + 'echeance': invoice.date_due, + 'ref_piece': invoice.number + }) + invoice.update({ + 'pca_is_exported': True, + 'pca_export_date': fields.Datetime.now() + }) + + company_name = request.env['res.company'].search([('id', '=', 1)]).name + filename_ = (company_name.title().replace(' ', '') + + 'ExportPCA_' + date_start.replace('-', '') + + '_' + date_end.replace('-', '')) + + if format == 'csv': + return self.export_csv(lignes_export, header, filename_) + + return self.export_xls(lignes_export, header, filename_) + + @http.route('/web/export_pca/', type='http', auth="user") + @serialize_exception + def export_pca(self, format, type, date_start, date_end, **kw): + return self.datas_export_ventes(format, type, date_start, date_end,) diff --git a/wizard/pca_fae_export_wizard.xml b/wizard/pca_fae_export_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..ae76e555a52a52c1e7fbbb9e9097bcfc736ca39b --- /dev/null +++ b/wizard/pca_fae_export_wizard.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<odoo> + <record id="pca_export_wizard_view_form" model="ir.ui.view"> + <field name="name">pca.export.wizard.form</field> + <field name="model">pca.export.wizard</field> + <field name="arch" type="xml"> + <form string="Export des données"> + <group> + <group> + <field name="export_format" widget="radio" /> + </group> + <group> + <field name="export_type" widget="radio" /> + </group> + </group> + + <group> + <group> + <field name="date_start"/> + </group> + <group> + <field name="date_end"/> + </group> + </group> + <footer> + <button class="btn btn-sm btn-primary" name="get_data_file" string="Télécharger" type="object"/> + <button class="btn btn-sm btn-default" special="cancel" string="Fermer"/> + </footer> + </form> + </field> + </record> + + <record id="pca_export_wizard_action" model="ir.actions.act_window"> + <field name="name">Export écritures PCA</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">pca.export.wizard</field> + <field name="view_mode">form</field> + <field name="view_id" ref="pca_export_wizard_view_form"/> + <field name="target">new</field> + </record> + + <menuitem action="pca_export_wizard_action" + id="menu_pca_export_wizardt" + name="Export écritures PCA" + parent="lefilament_pca_fae.pca_fae_root_menu" + sequence="30" /> +</odoo>