diff --git a/__manifest__.py b/__manifest__.py index e94acb035285e8635b361d4c532223a1d68db95a..bae454ae4167a10b5ec338e49ae2238d661d3907 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -19,12 +19,14 @@ 'security/ir.model.access.csv', 'datas/adefpat_archi_dossier_datas.xml', 'wizard/upload_file_wizard.xml', + "wizard/adefpat_project_justif_zip_wizard.xml", 'views/project_views.xml', 'views/project_task_views.xml', 'views/adefpat_list_type_doc_views.xml', 'views/hr_timesheet_views.xml', 'views/hr_expense_views.xml', 'views/res_company.xml', + 'views/account_views.xml', ], } \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py index 33cba6ae53e3b77bd02007ec2c94c4ac78eae3e3..7f6fb42173d401be7161d7e564640f160579c499 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020 Le Filament (<http://www.le-filament.com>) +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import project @@ -6,4 +6,5 @@ from . import project_task from . import adefpat_list_type_doc from . import hr_expense from . import hr_timesheet -from . import res_company \ No newline at end of file +from . import res_company +from . import account \ No newline at end of file diff --git a/models/account.py b/models/account.py new file mode 100644 index 0000000000000000000000000000000000000000..7bb4d72a6d8aa313d28a12204aab958cdccc5e82 --- /dev/null +++ b/models/account.py @@ -0,0 +1,83 @@ +# Copyright 2020-2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from datetime import date + +from odoo import api, models, fields +from odoo.exceptions import UserError + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + proof_file = fields.Char("Facture fournisseur") + + # ------------------------------------------------------ + # Override ORM + # ------------------------------------------------------ + @api.multi + def unlink(self): + for line in self: + if line.proof_file: + raise UserError( + "Un document est attaché à cette ligne, " + "veuillez le supprimer avant de supprimer la ligne.") + return super(AccountInvoice, self).unlink() + + # ------------------------------------------------------ + # Button Function + # ------------------------------------------------------ + @api.multi + def get_content_details_url(self): + # TODO: Refaire la fonction sans le type CMIS Folder + """ + Ouvre une nouvelle fenêtre avec le fichier + :return: ir.actions.act_url + """ + backend = self.env['cmis.backend'].search([], limit=1) + for pp in self: + if pp.proof_file: + properties = backend.get_cmis_repository().getFolder( + pp.proof_file).getProperties() + url = backend.get_content_details_url_from_props(properties) + return { + 'type': 'ir.actions.act_url', + 'url': url, + 'target': 'new', + } + + def delete_file(self): + backend = self.env['cmis.backend'].search([], limit=1) + try: + file = backend.get_cmis_repository().getObject( + self.proof_file + ) + file.delete() + self.proof_file = False + except: + self.proof_file = False + + # ------------------------------------------------------ + # Common Function + # ------------------------------------------------------ + def get_file_properties(self): + """ + Ajoute les propriétés au dossier lors de la création dans Alfresco + """ + if self.task_ids: + task_start = self.task_ids.sorted(key=lambda r: r.date_deadline)[0].date_deadline + task_end = self.task_ids.sorted(key=lambda r: r.date_deadline, reverse=True)[0].date_deadline + return { + 'cmis:secondaryObjectTypeIds': ['P:adefpat:facture'], + 'adefpat:factureStartDate': fields.Datetime.to_datetime(task_start).isoformat(), + 'adefpat:factureEndDate': fields.Datetime.to_datetime(task_end).isoformat(), + 'adefpat:factureTypeConvention': self.type_convention_id.name or "", + } + else: + return { + 'cmis:secondaryObjectTypeIds': ['P:adefpat:facture'], + 'adefpat:factureStartDate': fields.Datetime.to_datetime( + self.date_invoice).isoformat(), + 'adefpat:factureTypeConvention': self.type_convention_id.name or "", + } + diff --git a/models/res_company.py b/models/res_company.py index 48cb2357fac274c4a6f1e50d69b6349a7585a67d..d0bfcc37e7213b748dc99e9dfbbd775453e95ee7 100644 --- a/models/res_company.py +++ b/models/res_company.py @@ -9,3 +9,6 @@ class ResCompanyAlfodoo(models.Model): project_cmis = fields.Char("NodeRef Projets") proof_cmis = fields.Char("NodeRef Justificatifs") + proof_invoice_cmis = fields.Char("NodeRef Justificatifs Facture Fournisseur") + proof_invoice_customer_cmis = fields.Char("NodeRef Justificatifs Facture Client") + proof_justif_cmis = fields.Char("NodeRef Dossiers pour JSON") diff --git a/views/account_views.xml b/views/account_views.xml index 2aad9988154457b69894a9a4d758fba9dad14073..28b2d8a90477e5336732ff1161f5288bf2dc8e3c 100644 --- a/views/account_views.xml +++ b/views/account_views.xml @@ -1,8 +1,63 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> <data> - - $END$ - + + <record id="invoice_supplier_form" model="ir.ui.view"> + <field name="name">account.invoice.supplier.form.inherit</field> + <field name="model">account.invoice</field> + <field name="inherit_id" ref="account.invoice_supplier_form"/> + <field name="arch" type="xml"> + <header position="inside"> + <field name="proof_file" invisible="1"/> + <button name="%(adefpat_alfodoo.upload_file_wizard_action)d" + string="Charger le justificatif" + type="action" + class="oe_highlight" + attrs="{'invisible': [('proof_file', '!=', False)]}" + /> + <button + name="delete_file" + string="Supprimer le fichier" + type="object" + attrs="{'invisible': [('proof_file', '=', False)]}" + /> + <button + name="get_content_details_url" + string="Voir le fichier dans Alfresco" + type="object" + attrs="{'invisible': [('proof_file', '=', False)]}" + /> + </header> + </field> + </record> + + <record id="invoice_form" model="ir.ui.view"> + <field name="name">adefpat.invoice_form.form.alfodoo</field> + <field name="model">account.invoice</field> + <field name="inherit_id" ref="account.invoice_form"/> + <field name="arch" type="xml"> + <header position="inside"> + <field name="proof_file" invisible="1"/> + <button name="%(adefpat_alfodoo.upload_file_wizard_action)d" + string="Charger le justificatif" + type="action" + class="oe_highlight" + attrs="{'invisible': [('proof_file', '!=', False)]}" + /> + <button + name="delete_file" + string="Supprimer le fichier" + type="object" + attrs="{'invisible': [('proof_file', '=', False)]}" + /> + <button + name="get_content_details_url" + string="Voir le fichier dans Alfresco" + type="object" + attrs="{'invisible': [('proof_file', '=', False)]}" + /> + </header> + </field> + </record> </data> </odoo> \ No newline at end of file diff --git a/views/res_company.xml b/views/res_company.xml index f9f7a2fc33e2b9762882a641daa319aa1738a9b7..a6e8d804cd5e6e03113af54e031fe28fa2568280 100644 --- a/views/res_company.xml +++ b/views/res_company.xml @@ -15,6 +15,9 @@ <group> <field name="project_cmis"/> <field name="proof_cmis"/> + <field name="proof_invoice_cmis"/> + <field name="proof_invoice_customer_cmis"/> + <field name="proof_justif_cmis"/> </group> </group> </page> diff --git a/wizard/__init__.py b/wizard/__init__.py index eb1ffab4f53eeb4bf641982d70390d62d949cf63..3d23381f6e383e6647db22ae2bbacfc83ee8a1cb 100644 --- a/wizard/__init__.py +++ b/wizard/__init__.py @@ -2,3 +2,4 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import upload_file_wizard +from . import adefpat_project_justif_zip_wizard diff --git a/wizard/adefpat_project_justif_zip_wizard.py b/wizard/adefpat_project_justif_zip_wizard.py new file mode 100644 index 0000000000000000000000000000000000000000..de55af7405234aac16f6cfdca4b0223301a4f588 --- /dev/null +++ b/wizard/adefpat_project_justif_zip_wizard.py @@ -0,0 +1,223 @@ +# Copyright 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import json + +from datetime import date +from odoo import api, fields, models + +from odoo.exceptions import UserError + + +class AdefpatProjectJustifZip(models.TransientModel): + _name = "adefpat.project.justif.zip.wizard" + _description = "Wizard justifs zip projets" + + period_start = fields.Date("Début de période", required=True) + period_end = fields.Date( + "Fin de période", required=True, default=fields.Date.today() + ) + type_convention_id = fields.Many2one( + "adefpat.type.convention", + domain=[ + "|", + ("date_end_validity", ">=", fields.Date.today()), + ("date_end_validity", "=", False), + ], + string="Type de convention de financement", + ) + + # function will trigger from the show report button + @api.multi + def build_folder(self): + # Get Projects with at least 1 task on period + project_ids = self.env["project.task"].search([ + ("date_deadline", "<=", self.period_end), + ("date_deadline", ">=", self.period_start), + ]).mapped("project_id") + + if self.type_convention_id: + project_ids = project_ids.filtered( + lambda p: p.type_convention_id == self.type_convention_id) + + compteur = 0 + json_file = [] + for project in project_ids: + compteur += 1 + path = "PATH:'/app:company_home/st:sites/cm:odoo/cm:documentLibrary/cm:Projets/" + project.name + # virtual_json["nodes"]["nodes"].append({ + json_file.append({ + "id": "1" + str(compteur), + "name": project.name, + "description": project.name, + "nodes":[ + { + "id": "1" + str(compteur) + "1", + "name": "CR séance", + "description": "Compte-Rendus des séances d’accompagnement", + "search": { + "language": "fts-alfresco", + "query": path + "/cm:seances//*'" + " AND =adefpat:typeDocument:'compte rendu'", + } + }, + { + "id": "1" + str(compteur) + "2", + "name": "GAP", + "description": "Compte-Rendus des séances de GAP", + "search": { + "language": "fts-alfresco", + "query": path + "/cm:gap//*'" + " AND =adefpat:typeDocument:'compte rendu'", + }, + }, + { + "id": "1" + str(compteur) + "3", + "name": "Evaluation CF", + "description": "Evaluation finale du consultant formateur", + "search": { + "language": "fts-alfresco", + "query": path + "/cm:seances//*'" + " AND =adefpat:typeDocument:'évaluation du formateur'", + }, + }, + { + "id": "1" + str(compteur) + "4", + "name": "Evaluation stagiaire", + "description": "Questionnaire de satisfaction de chaque stagiaire", + "search": { + "language": "fts-alfresco", + "query": path + "/cm:beneficiaires//*'" + " AND =adefpat:typeDocument:'questionnaire de satisfaction'", + }, + }, + { + "id": "1" + str(compteur) + "5", + "name": "Consultations", + "description": "Consultations", + "search": { + "language": "fts-alfresco", + "query": path + "/cm:consultation//*'", + }, + }, + { + "id": "1" + str(compteur) + "6", + "name": "Conventions", + "description": "Conventions", + "search": { + "language": "fts-alfresco", + "query": path + "/cm:admin//*'" + " AND (=adefpat:typeDocument:'convention d'accompagnement' OR =adefpat:typeDocument:'convention d'objectif')", + }, + }, + ]}) + + virtual_json = { + "name": "Justificatifs Projets", + "nodes": [{ + "id": "1", + "name": "Justificatifs Projets", + "description": "Justificatifs Projets", + "nodes": json_file, + }] + } + + js = json.dumps(virtual_json, indent=4) + prop = { + 'cmis:secondaryObjectTypeIds': ['P:adefpat:justificatif'], + } + # 'adefpat:financementConvention': str(self.type_convention_id.name), + backend = self.env['cmis.backend'].search([], limit=1) + json_file_cmis = self.env.user.company_id.proof_justif_cmis + repo = backend.get_cmis_repository().getObject(json_file_cmis) + try: + file = repo.createDocument( + name="justifs_projets_"+ str(date.today()) +".json", + properties=prop, + contentFile=js, + contentType="application/json" + ) + except Exception as e: + raise UserError(json.loads(e.details).get('message')) + + @api.multi + def build_facture_fournisseur(self): + + virtual_json = { + "name" : "Factures fournisseurs", + "nodes" : [{ + "id" : "1", + "name" : "Factures fournisseurs", + "description" : "Factures fournisseurs", + "search" : { + "language" : "fts-alfresco", + "query" : "(PATH:'/app:company_home/st:sites/cm:odootest/cm:documentLibrary/cm:Factures_x0020_Justifs//*') AND (+adefpat:factureEndDate:['" + str(self.period_start) + "' TO '" + str(self.period_end) +"'] OR +adefpat:factureSartDate:['" + str(self.period_start) + "' TO '" + str(self.period_end) +"'])", + } + }, + ] + } + + js = json.dumps(virtual_json, indent=4) + prop = { + 'cmis:secondaryObjectTypeIds': ['P:adefpat:justificatif'], + } + + backend = self.env['cmis.backend'].search([], limit=1) + json_file_cmis = self.env.user.company_id.proof_justif_cmis + repo = backend.get_cmis_repository().getObject(json_file_cmis) + try: + file = repo.createDocument( + name="justifs_factures_fournisseurs_"+ str(date.today()) +".json", + properties=prop, + contentFile=js, + contentType="application/json" + ) + except Exception as e: + raise UserError(json.loads(e.details).get('message')) + + @api.multi + def build_facture_client(self): + + if self.type_convention_id: + virtual_json = { + "name":"Factures clients", + "nodes":[{ + "id":"1", + "name":"Factures clients", + "description":"Factures clients", + "search":{ + "language":"fts-alfresco", + "query":"(PATH:'/app:company_home/st:sites/cm:odootest/cm:documentLibrary/cm:Factures_x0020_clients_x0020_Justifs//*') AND (+adefpat:factureStartDate:['" + str(self.period_start) + "' TO '" + str(self.period_end) +"']) AND =adefpat:factureTypeConvention:'"+ self.type_convention_id.name +"'", + } + }, + ] + } + else: + virtual_json = { + "name": "Factures clients", + "nodes": [{ + "id": "1", + "name": "Factures clients", + "description": "Factures clients", + "search": { + "language": "fts-alfresco", + "query": "(PATH:'/app:company_home/st:sites/cm:odootest/cm:documentLibrary/cm:Factures_x0020_clients_x0020_Justifs//*') AND (+adefpat:factureStartDate:['" + str( + self.period_start) + "' TO '" + str( + self.period_end) + "'])", + } + }, + ] + } + + js = json.dumps(virtual_json, indent=4) + prop = { + 'cmis:secondaryObjectTypeIds': ['P:adefpat:justificatif'], + } + + backend = self.env['cmis.backend'].search([], limit=1) + json_file_cmis = self.env.user.company_id.proof_justif_cmis + repo = backend.get_cmis_repository().getObject(json_file_cmis) + try: + file = repo.createDocument( + name="justifs_factures_clients_"+ str(date.today()) +".json", + properties=prop, + contentFile=js, + contentType="application/json" + ) + except Exception as e: + raise UserError(json.loads(e.details).get('message')) diff --git a/wizard/adefpat_project_justif_zip_wizard.xml b/wizard/adefpat_project_justif_zip_wizard.xml new file mode 100644 index 0000000000000000000000000000000000000000..d886017c0ce3474574fb6a3852a6964c25932956 --- /dev/null +++ b/wizard/adefpat_project_justif_zip_wizard.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- Copyright 2022 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> +<odoo> + <record id="adefpat_project_justif_zip_wizard_form" model="ir.ui.view"> + <field name="name">adefpat.project.justif.zip.wizard.form</field> + <field name="model">adefpat.project.justif.zip.wizard</field> + <field name="arch" type="xml"> + <form string="Génération des justifs ZIP"> + <group> + <group> + <field name="period_start" /> + <field name="period_end" /> + <field name="type_convention_id" /> + </group> + </group> + <footer> + <button + class="btn btn-sm btn-primary" + name="build_folder" + string="Justifs par dossier" + type="object" + /> + <button + class="btn btn-sm btn-primary" + name="build_facture_fournisseur" + string="Justifs factures fournisseurs" + type="object" + /> + <button + class="btn btn-sm btn-primary" + name="build_facture_client" + string="Justifs factures client" + type="object" + /> + <button + class="btn btn-sm btn-default" + special="cancel" + string="Fermer" + /> + </footer> + </form> + </field> + </record> + + <record id="adefpat_project_justif_zip_action" model="ir.actions.act_window"> + <field name="name">Génération des justifs ZIP</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">adefpat.project.justif.zip.wizard</field> + <field name="view_id" ref="adefpat_project_justif_zip_wizard_form" /> + <field name="target">new</field> + </record> + + <menuitem + id="adefpat_project.menu_generate_zip" + name="Génération des justifs ZIP" + parent="project.menu_project_report" + action="adefpat_project_justif_zip_action" + sequence="30" + /> +</odoo> diff --git a/wizard/upload_file_wizard.py b/wizard/upload_file_wizard.py index 9b63d288b6adccca2c7c3eac083666010bd6d7c3..f7eb67e462f90a7bf7c4112134b57de1993127d7 100644 --- a/wizard/upload_file_wizard.py +++ b/wizard/upload_file_wizard.py @@ -26,8 +26,15 @@ class UploadFileWizard(models.TransientModel): Ajoute un fichier sur la GED Alfresco @return: fonction get_partner_files() de res.partner """ - # Get proof folder nodeRef - proof_folder = self.env.user.company_id.proof_cmis + if self.env.context["active_model"] == "account.invoice": + account_id = self.env["account.invoice"].browse(self.env.context["active_id"]) + if account_id.type == "in_invoice": + proof_folder = self.env.user.company_id.proof_invoice_cmis + else: + proof_folder = self.env.user.company_id.proof_invoice_customer_cmis + else: + # Get proof folder nodeRef + proof_folder = self.env.user.company_id.proof_cmis if not proof_folder: raise UserError("Le dossier des justificatifs n'est pas configuré") @@ -51,9 +58,14 @@ class UploadFileWizard(models.TransientModel): print("--- (self.file) ---", str(self.file)) if not obj.proof_file: try: - filename = (fields.Date.to_string(obj.date).replace('-', '') + + if self.env.context["active_model"] == "account.invoice": + filename = (fields.Date.to_string(obj.date_invoice).replace('-', '') + + '-' + str(obj.id) + ' - ' + self.filename) + else: + filename = (fields.Date.to_string(obj.date).replace('-', '') + '-' + str(obj.id) + ' - ' + obj.employee_id.user_id.name + ' - ' + self.filename) + file = repo.createDocument( name=filename, properties=obj.get_file_properties(),