# Copyright 2020 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from datetime import datetime, timedelta from odoo import models, fields, api from odoo.tools.misc import xlwt import csv import base64 HEADER_MEMBER = [ 'Nom', 'Prénom', 'Commune', 'Mobile', 'Fixe', 'Email' ] class Project(models.Model): _inherit = "project.project" @api.model def _default_explication_financement(self): if self.env.user.company_id.explication_financement: return self.env.user.company_id.explication_financement privacy_visibility = fields.Selection([ ('followers', 'On invitation only'), ('employees', 'Visible by all employees'), ('portal', 'Visible by following customers'), ], string='Confidentialité', required=True, default='employees') # Champs étape "Demande" => Vue générale name_subtitle = fields.Char("Sous-titre") first_contact_id = fields.Many2one( 'res.partner', string='1er contact', domain=[ ('active', '=', True), ('is_company', '=', False)], on_delete='restrict') type_contact = fields.Selection([ ('adherent', 'Adhérent'), ('no_adherent', 'Non adhérent'), ('pdp', 'Porteur de projet'), ], string="Type contact") date_first_contact = fields.Date( "Date de 1er contact", default=fields.Date.today) prescripteur_id = fields.Many2one( 'res.partner', string='Prescripteur', domain=[ ('active', '=', True), ('is_company', '=', False)], on_delete='restrict') porteurs_projets_ids = fields.One2many( 'res.partner.porteur.project', 'project_id', string="Porteurs de projet") description = fields.Text("Description") territoire_id = fields.Many2one( 'res.partner', string='Territoire', domain=[ ('active', '=', True), ('is_company', '=', True), '|', ('type_structure_id.name', '=', 'ComCom'), ('type_structure_id.name', '=', 'PETR/PNR')], on_delete='restrict') departement_ids = fields.Many2many( comodel_name='res.partner', relation='res_partner_departement_rel', column1='partner_id', column2='departement_id', domain=[ ('active', '=', True), ('is_company', '=', True), ('type_structure_id.name', '=', 'Département')], string="Départements", related='territoire_id.departement_ids') region_ids = fields.Many2many( comodel_name='res.partner', relation='res_partner_region_rel', column1='partner_id', column2='region_id', domain=[ ('active', '=', True), ('is_company', '=', True), ('type_structure_id.name', '=', 'Région')], string="Régions", related='territoire_id.region_ids') petr_ids = fields.Many2many( comodel_name='res.partner', relation='res_partner_petr_rel', column1='partner_id', column2='petr_id', domain=[ ('active', '=', True), ('is_company', '=', True), ('type_structure_id.name', '=', 'PETR/PNR')], string="PETR/PNR", related='territoire_id.petr_ids') departement = fields.Selection([ ('12', '12'), ('34', '34'), ('46', '46'), ('48', '48'), ('81', '81'), ('82', '82') ], string="Département du projet") user_id = fields.Many2one( 'res.users', string='CFD', default=lambda self: self.env.user, track_visibility="onchange") # Champs étape "Instruction" => Vue générale od_ids = fields.Many2many( comodel_name='res.partner', column1='project_id', column2='od_id', relation='res_partner_od_rel', string='OD', domain=[ ('active', '=', True), ('is_company', '=', True)], on_delete='restrict') elu_id = fields.Many2one( 'res.partner', string='Elu qui présente le dossier', domain=[ ('active', '=', True), ('is_company', '=', False)], on_delete='restrict') animateur_ids = fields.Many2many( comodel_name = 'res.partner', column1='project_id', column2='anim_id', relation='res_partner_anim_rel', string='Animateurs', domain=[ ('active', '=', True), ('is_company', '=', False)], on_delete='restrict') 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") date_ca = fields.Date("Date de CA") date_ca_next = fields.Date("Lendemain date de CA", compute='_compute_date_ca_next') date_demarrage = fields.Date("Date de démarrage prévisionnel") # Champs étape "Instruction" => Onglet Dossier contexte = fields.Text("Contexte projet") caract_beneficiaire = fields.Text("Caractéristiques du bénéficiaire") historique = fields.Text("Historique & Aujourd’hui") besoins_beneficiaires = fields.Text("Besoins des bénéficiaires") objectifs_accompagnement = fields.Text("Objectifs d’accompagnement") competences_requises = fields.Text("Compétences du CF") secteurs_requis = fields.Text("Secteurs d’activités") modalites_intervention = fields.Text("Modalités d'intervention") modalites_facturation = fields.Text("Modalités de facturation") modalites_reponse = fields.Text("Modalités de réponse") # Infos dossier CA type_beneficiaire = fields.Char("Type de bénéficiares", compute='_compute_type_beneficiaire') objectif_projet = fields.Selection( [ ('creation', "Création d'entreprise"), ('creation_activite', 'Création nouvelle activité'), ('consolidation', 'Consolidation'), ('emergence', 'Émergence'), ('operation', "Opération territoriale pour les entreprises"), ('post_creation', 'Post-création'), ('post_reprise', 'Post-reprise'), ('projet_inter', 'Projet inter-entreprise'), ('projet_structurant', 'Projet structurant territoire'), ('reprise', 'Reprise'), ('transmission', 'Transmission'), ], string="Objectif projet") secteur_crit = fields.Selection( [ ('agriculture', 'Agriculture'), ('agro_alimentaire', 'Agro-alimentaire'), ('artisanat', 'Artisanat - Commerce - Industrie'), ('culture', 'Culture'), ('environnement', 'Environnement'), ('intersectoriel', 'Intersectoriel'), ('service', 'Service'), ('service', 'Services à la population'), ('tourisme', 'Tourisme - Hôtellerie - Restauration'), ], string="Secteur d'activité") taille = fields.Selection( [ ('inf_10', "De 10 à 250"), ('sup_10', "< 10 salariés") ], string="Taille entreprise") objectif_formation = fields.Selection( [ ('def_projet', "Définition de projet"), ('def_modele_eco', "Définition d'un modèle économique"), ('def_strat_com', "Définition d'une stratégie de développement"), ('diagnostic', 'Diagnostic stratégique'), ('gestion_prod', 'Gestion de production'), ('gestion_financ', 'Gestion financière'), ('marketing', "Marketing - Commercialisation - Promotion - Communication"), ('management', 'Management - Organisation - RH'), ('orga_collective', 'Organisation collective'), ], string="Objectif formation") type_formation = fields.Selection( [ ('individuelle', 'Générale individuelle'), ('collective', 'Générale collective'), ('indiv_collect', 'Générale collective individualisée'), ], string="Type formation") encadrement = fields.Selection( [ ('minimis', 'De minimis'), ('neant', 'Neant'), ('encadrement', 'Encadrement des aides à la formation') ], string="Encadrement Aides") nb_activité = fields.Integer("Nombre d’activités ou entreprises concernées") nb_emplois = fields.Integer("Nombre d’emplois concernés") lieu = fields.Text("Lieu") periode_realisation = fields.Text("Période de réalisation") contenu_formation = fields.Text("Contenu des séances") methode_savoir = fields.Text("Méthode et savoirs transmis") travaux_intersessions = fields.Text("Travaux intersessions") # Info Budget nb_jours_adefpat = fields.Float("Nombre de jours CFD") cout_jour_adefpat = fields.Float("Coût jour CFD") nb_jour_theorique_tot = fields.Float( "Nombre de jours théoriques total", compute='_compute_nb_jour_theorique_tot') nb_jour_pratique_tot = fields.Float( "Nombre de jours pratiques total", compute='_compute_nb_jour_pratique_tot') nb_jour_plann = fields.Float( "Nombre de jours plannifiés", compute='_compute_nb_jour_plann') nb_jour_rest = fields.Float( "Nombre de jours restants", compute='_compute_nb_jour_rest') nb_jour_tot = fields.Float( "Nombre de jours total", compute='_compute_nb_jour_tot', store=True) nb_heure_tot = fields.Float( "Nombre d'heures' total", compute='_compute_nb_jour_tot', store=True) total_cout_adefpat = fields.Float( "Total coûts CFD", compute='_compute_total_cout_adefpat') financement_adefpat = fields.Float( "Financement Adefpat", compute='_compute_financement_adefpat') financement_ids = fields.One2many( 'adefpat.project.financement', 'project_id', string="Financements") total_financement = fields.Float("Total autres", compute='_compute_total_financment') cout_ids = fields.One2many( 'adefpat.project.cout', 'project_id', string="Coûts") total_cout = fields.Float("Total coûts", compute='_compute_total_cout') honoraire_intervenant = fields.Float( "Honoraires d'intervenants") total_budget_cout = fields.Float( "Total budget coûts", compute='_compute_total_budget_cout') total_budget_financement = fields.Float("Total budget financements", compute='_compute_total_budget_financement') explication_financement = fields.Text( "Explication financement porteur de projet", default=_default_explication_financement) # Champs étape "Instruction" => Onglet Consultation consulant_ids = fields.One2many( 'res.partner.consultants.project', 'project_id', string="Consultants") date_selection = fields.Date("Date de sélection") date_notification = fields.Date("Date de notification") date_cdc = fields.Date("Date d'envoi du CDC") # Champs étape "Instruction" => Onglet GAP elu_referent_id = fields.Many2one( 'res.partner', string='Élu référent', domain=[ ('active', '=', True), ('is_company', '=', False)], on_delete='restrict') membre_ids = fields.One2many( 'res.partner.membres.project', 'project_id', string="Membres") modalite_gap = fields.Text("Modalités GAP") reunion_ids = fields.One2many( 'adefpat.reunion.gap', 'project_id', string="Réunions") # Champs étape "Prêt pour CA" => Onglet Général num_dossier = fields.Char("Numéro de dossier") @api.depends('date_ca') @api.multi def _compute_date_ca_next(self): for project in self: if project.date_ca: project.date_ca_next = project.date_ca + timedelta(days=1) @api.depends('financement_ids', 'financement_ids.montant') @api.multi def _compute_total_financment(self): for project in self: total_financement = 0.0 for financement in project.financement_ids: total_financement += financement.montant project.total_financement = total_financement @api.depends('cout_ids', 'cout_ids.montant') @api.multi def _compute_total_cout(self): for project in self: total_cout = 0.0 for cout in project.cout_ids: total_cout += cout.montant project.total_cout = total_cout @api.depends('cout_ids', 'cout_ids.nb_jour_theorique') @api.multi def _compute_nb_jour_theorique_tot(self): for project in self: total_jour = 0.0 for cout in project.cout_ids: total_jour += cout.nb_jour_theorique project.nb_jour_theorique_tot = total_jour\ @api.depends('cout_ids', 'cout_ids.nb_jour_pratiques') @api.multi def _compute_nb_jour_pratique_tot(self): for project in self: total_jour = 0.0 for cout in project.cout_ids: total_jour += cout.nb_jour_pratiques project.nb_jour_pratique_tot = total_jour\ @api.depends('task_ids', 'task_ids.duree_hr') @api.multi def _compute_nb_jour_plann(self): for project in self: total_jour = 0.0 for task in project.task_ids: total_jour += task.duree_hr project.nb_jour_plann = total_jour\ @api.depends('nb_jour_plann', 'nb_jour_theorique_tot') @api.multi def _compute_nb_jour_rest(self): for project in self: project.nb_jour_rest = project.nb_jour_theorique_tot - project.nb_jour_plann @api.depends('cout_ids', 'cout_ids.nb_jour_theorique', 'cout_ids.nb_jour_pratiques') @api.multi def _compute_nb_jour_tot(self): for project in self: total_jour = 0.0 for cout in project.cout_ids: total_jour += cout.nb_jour_theorique + cout.nb_jour_pratiques project.nb_jour_tot = total_jour project.nb_heure_tot = total_jour * 7 @api.depends('nb_jours_adefpat', 'cout_jour_adefpat') @api.multi def _compute_total_cout_adefpat(self): for project in self: project.total_cout_adefpat = project.nb_jours_adefpat * project.cout_jour_adefpat @api.depends('total_cout_adefpat', 'total_cout') @api.multi def _compute_total_budget_cout(self): for project in self: project.total_budget_cout = project.total_cout_adefpat + project.total_cout @api.depends('total_budget_cout', 'total_financement') @api.multi def _compute_financement_adefpat(self): for project in self: project.financement_adefpat = project.total_budget_cout - project.total_financement @api.depends('financement_adefpat', 'total_financement') @api.multi def _compute_total_budget_financement(self): for project in self: project.total_budget_financement = project.financement_adefpat + project.total_financement @api.depends('porteurs_projets_ids', 'porteurs_projets_ids.statut') @api.multi def _compute_type_beneficiaire(self): for project in self: project.type_beneficiaire = ', '.join(project.porteurs_projets_ids.mapped('statut.name')) @api.multi def validate_ca(self): for project in self: project.num_dossier = project.departement + '/' + datetime.strftime(datetime.today(), '%y') + '/' + self.env['ir.sequence'].next_by_code( 'increment_num_dossier') def get_workbook(self, filename_, project_id): project = self.env['project.project'].search([('id', '=', project_id)]) workbook = xlwt.Workbook() worksheet = workbook.add_sheet(filename_) gap_ids = project.membre_ids header_file = HEADER_MEMBER 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') cell_style = base_style row_index = 1 for gap_id in gap_ids: worksheet.write(row_index, 0, str(gap_id.lastname), cell_style) worksheet.write(row_index, 1, str(gap_id.firstname), cell_style) worksheet.write(row_index, 2, str(gap_id.commune), cell_style) worksheet.write(row_index, 3, str(gap_id.mobile), cell_style) worksheet.write(row_index, 4, str(gap_id.fixe), cell_style) worksheet.write(row_index, 5, str(gap_id.email), cell_style) row_index = row_index + 1 return workbook @api.multi def export_gap(self): for project in self: filename_ = project.name project_id = project.id return { 'type': 'ir.actions.act_url', 'url': '/web/export_gap?filename_=%s&project_id=%s' % (filename_, project_id), 'target': 'new', } class AdefpatTypeConvention(models.Model): _name = 'adefpat.type.convention' _description = 'Liste type de convention' name = fields.Char( string="Convention", required=True, ) date_end_validity = fields.Date("Date de fin de validité") class AdefpatReunionGAP(models.Model): _name = 'adefpat.reunion.gap' _description = 'Réunions GAP' _rec_name = 'date' # document_ids = fields.One2many('ir.attachment', string="Documents") date = fields.Date( string="Date du GAP", required=True, ) project_id = fields.Many2one( 'project.project', string='Projet', default=lambda self: self.env.context.get('default_project_id')) class AdefpatMembresProjets(models.Model): _name = 'res.partner.membres.project' _description = 'Membres' partner_id = fields.Many2one( 'res.partner', string='Membre', domain=[ ('active', '=', True), ('is_company', '=', False)], required=True, on_delete='restrict') lastname = fields.Char( related='partner_id.lastname', string="Nom", store=False) firstname = fields.Char( related='partner_id.firstname', string="Prénom", store=False) commune = fields.Char( related='partner_id.city', string="Commune", store=False) mobile = fields.Char( related='partner_id.mobile', string="Mobile", store=False) fixe = fields.Char( related='partner_id.phone', string="Fixe", store=False) email = fields.Char( related='partner_id.email', string="Email", store=False) project_id = fields.Many2one( 'project.project', string='Projet', default=lambda self: self.env.context.get('default_project_id')) class AdefpatConsultantsProjets(models.Model): _name = 'res.partner.consultants.project' _description = 'Consultants' _rec_name = 'name' partner_id = fields.Many2one( 'res.partner', string='Consultant', domain=[ ('active', '=', True), ('is_company', '=', False), ('is_consultant_form', '=', True), '|', ('reference', '=', 'reference'), ('reference', '=', 'prereference')], required=True, on_delete='restrict') name = fields.Char(related='partner_id.name') lastname = fields.Char( related='partner_id.lastname', string="Nom", store=False) firstname = fields.Char( related='partner_id.firstname', string="Prénom", store=False) mobile = fields.Char( related='partner_id.mobile', string="Mobile", store=False) email = fields.Char( related='partner_id.email', string="Email", store=False) reference = fields.Selection([ ('reference', 'Référencé'), ('prereference', 'Pré-Référencé'), ('dereference', 'Dé-Référencé')], related='partner_id.reference', string="Référencement", store=False) cout_journée = fields.Float("Coût journée") is_selected = fields.Boolean("Est sélectionné") project_id = fields.Many2one( 'project.project', string='Projet', default=lambda self: self.env.context.get('default_project_id')) class AdefpatPorteursProjets(models.Model): _name = 'res.partner.porteur.project' _description = 'Porteurs de projets' _rec_name = 'name' porteur_id = fields.Many2one( 'res.partner', string='Porteur de projet', domain=[('is_company', '=', False)], required=True, on_delete='restrict' ) name = fields.Char(related='porteur_id.name') lastname = fields.Char( related='porteur_id.lastname', string="Nom", store=False) firstname = fields.Char( related='porteur_id.firstname', string="Prénom", store=False) commune = fields.Char( compute='_compute_commune', string="Commune", store=False) mobile = fields.Char( related='porteur_id.mobile', string="Mobile", store=False) fixe = fields.Char( related='porteur_id.phone', string="Fixe", store=False) email = fields.Char( related='porteur_id.email', string="Email", store=False) statut = fields.Many2one( 'adefpat.project.statut', string="Statut") eligible = fields.Boolean( string="Eligible") is_present = fields.Boolean("Est présent") task_id = fields.Many2one( 'project.task', string='Séance', default=lambda self: self.env.context.get('default_task_id')) project_id = fields.Many2one( 'project.project', string='Projet', default=lambda self: self.env.context.get('default_project_id')) @api.depends('porteur_id') def _compute_commune(self): for r in self: if r.porteur_id.city: r.commune = r.porteur_id.city.lower() class AdefpatStatut(models.Model): _name = 'adefpat.project.statut' _description = 'Statut' name = fields.Char("Statut", required=True,) class AdefpatFinancement(models.Model): _name = 'adefpat.project.financement' _description = 'Financements' partner_id = fields.Many2one( 'res.partner', string='Facturation', domain=[ ('active', '=', True)], required=True, on_delete='restrict' ) montant = fields.Float("Montant") project_id = fields.Many2one( 'project.project', string='Projet', default=lambda self: self.env.context.get('default_project_id')) class AdefpatCout(models.Model): _name = 'adefpat.project.cout' _description = 'Coûts' module = fields.Char("Nom du module") partner_id = fields.Many2one( 'res.partner', string='Consultant', domain=[ ('active', '=', True), ('is_company', '=', False), ('is_consultant_form', '=', True)], on_delete='restrict',) nb_jour_theorique = fields.Float("Nombre de jours théoriques") nb_jour_pratiques = fields.Float("Nombre de jours pratiques") cout_jour = fields.Float("Coût jour") montant = fields.Float("Montant", compute='_compute_montant', store=True) project_id = fields.Many2one( 'project.project', string='Projet', default=lambda self: self.env.context.get('default_project_id')) @api.depends('cout_jour', 'nb_jour_theorique') def _compute_montant(self): for r in self: r.montant = r.cout_jour * r.nb_jour_theorique\