diff --git a/morpheus_contacts/__manifest__.py b/morpheus_contacts/__manifest__.py index 78a0d5b..0532117 100644 --- a/morpheus_contacts/__manifest__.py +++ b/morpheus_contacts/__manifest__.py @@ -20,5 +20,5 @@ ], 'installable': True, 'application': False, - 'auto_install': False, + 'auto_install': True, } \ No newline at end of file diff --git a/morpheus_contacts/__pycache__/__init__.cpython-312.pyc b/morpheus_contacts/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 685f6fe..0000000 Binary files a/morpheus_contacts/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/morpheus_contacts/models/__pycache__/__init__.cpython-312.pyc b/morpheus_contacts/models/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 8d977ee..0000000 Binary files a/morpheus_contacts/models/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/morpheus_contacts/models/__pycache__/res_partner.cpython-312.pyc b/morpheus_contacts/models/__pycache__/res_partner.cpython-312.pyc deleted file mode 100644 index c48c2b9..0000000 Binary files a/morpheus_contacts/models/__pycache__/res_partner.cpython-312.pyc and /dev/null differ diff --git a/morpheus_contacts/models/res_partner.py b/morpheus_contacts/models/res_partner.py index 2c5c33b..5d86da8 100644 --- a/morpheus_contacts/models/res_partner.py +++ b/morpheus_contacts/models/res_partner.py @@ -71,6 +71,24 @@ class ResPartner(models.Model): 'fornitore.attuale.option', string="Fornitori Attuali dei Clienti" ) + + # Campo per identificare se è un agente + is_agent = fields.Boolean( + string="È un Agente", + help="Spunta questa casella se questo contatto è un agente" + ) + + # Campo Email 2 + email2 = fields.Char( + string="E-mail 2" , + help="Indirizzo email secondario" + ) + + # Campo Note Logistiche (simile alle note interne) + note_logistiche = fields.Html( + string="Note Logistiche", + help="Note logistiche interne con editor ricco" + ) class ResPartnerSector(models.Model): _name = 'res.partner.sector' # Nome tecnico corretto diff --git a/morpheus_contacts/views/res_partner_view.xml b/morpheus_contacts/views/res_partner_view.xml index 0f8418d..c7461a5 100644 --- a/morpheus_contacts/views/res_partner_view.xml +++ b/morpheus_contacts/views/res_partner_view.xml @@ -8,6 +8,12 @@ + + + + + + @@ -35,6 +41,11 @@ + + + + + diff --git a/morpheus_crm/__manifest__.py b/morpheus_crm/__manifest__.py index 783dba0..fc2fe79 100644 --- a/morpheus_crm/__manifest__.py +++ b/morpheus_crm/__manifest__.py @@ -14,4 +14,5 @@ ], 'installable': True, 'application': False, + 'auto_install': True, } diff --git a/morpheus_crm/__pycache__/__init__.cpython-312.pyc b/morpheus_crm/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index bf1f6b9..0000000 Binary files a/morpheus_crm/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/morpheus_crm/models/__pycache__/__init__.cpython-312.pyc b/morpheus_crm/models/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 805a587..0000000 Binary files a/morpheus_crm/models/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/morpheus_crm/models/__pycache__/crm_lead.cpython-312.pyc b/morpheus_crm/models/__pycache__/crm_lead.cpython-312.pyc deleted file mode 100644 index a580d6a..0000000 Binary files a/morpheus_crm/models/__pycache__/crm_lead.cpython-312.pyc and /dev/null differ diff --git a/morpheus_crm/models/__pycache__/crm_lead_category.cpython-312.pyc b/morpheus_crm/models/__pycache__/crm_lead_category.cpython-312.pyc deleted file mode 100644 index f22d021..0000000 Binary files a/morpheus_crm/models/__pycache__/crm_lead_category.cpython-312.pyc and /dev/null differ diff --git a/morpheus_crm/models/crm_lead.py b/morpheus_crm/models/crm_lead.py index 0c4d9e3..c686af8 100644 --- a/morpheus_crm/models/crm_lead.py +++ b/morpheus_crm/models/crm_lead.py @@ -43,7 +43,36 @@ class CrmLead(models.Model): # Questo messaggio potrebbe apparire se la condizione 'invisible' nell'XML non fosse perfetta # o se il metodo fosse chiamato da un'altra parte. raise UserError(_("L'opportunità deve essere nello stadio 'Nuova' per poterla inviare a 'Wishlist verificata'.")) - + + # --- Invio email agli addetti acquisti --- + emails = [] + if self.addetto_acquisti and self.addetto_acquisti.partner_id.email: + emails.append(self.addetto_acquisti.partner_id.email) + if self.addetto_acquisti_2 and self.addetto_acquisti_2.partner_id.email: + emails.append(self.addetto_acquisti_2.partner_id.email) + if not emails: + return True # Nessun destinatario, esci silenziosamente + + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + opportunity_url = f"{base_url}/web#id={self.id}&model=crm.lead&view_type=form&page=wishlist_cliente" + button_html = f'Apri wishlist cliente' + body_html = f""" + Gentile Ufficio acquisti, + è stata verificata la wishlist presente nella seguente opportunità: + Nome cliente: {self.partner_id.name} + Nome opportunità: {self.name} + + {button_html} + """ + mail_values = { + 'subject': f"Wishlist verificata - {self.name}", + 'body_html': body_html, + 'email_to': ','.join(emails), + 'auto_delete': True, + 'model': 'crm.lead', + 'res_id': self.id, + } + self.env['mail.mail'].sudo().create(mail_values).send() return True @@ -75,15 +104,16 @@ class CrmLead(models.Model): self.agente_id = self.partner_id.agente_id self.percent_provvigioni = self.partner_id.percent_provvigioni - @api.model - def create(self, vals): - if vals.get('partner_id'): - partner = self.env['res.partner'].browse(vals['partner_id']) - if not vals.get('agente_id') and partner.agente_id: - vals['agente_id'] = partner.agente_id.id - if not vals.get('percent_provvigioni'): - vals['percent_provvigioni'] = partner.percent_provvigioni - return super(CrmLead, self).create(vals) + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get('partner_id'): + partner = self.env['res.partner'].browse(vals['partner_id']) + if not vals.get('agente_id') and partner.agente_id: + vals['agente_id'] = partner.agente_id.id + if not vals.get('percent_provvigioni'): + vals['percent_provvigioni'] = partner.percent_provvigioni + return super(CrmLead, self).create(vals_list) # Campi per la Richiesta Offerta diff --git a/morpheus_crm/views/crm_wishlist_view.xml b/morpheus_crm/views/crm_wishlist_view.xml index 0496370..25bb6d6 100644 --- a/morpheus_crm/views/crm_wishlist_view.xml +++ b/morpheus_crm/views/crm_wishlist_view.xml @@ -27,7 +27,7 @@ - + + + + + Richieste fornitori + supplier.request + RIF + 4 + 1 + 1 + + \ No newline at end of file diff --git a/morpheus_fornitori/data/supplier_request_stage_data.xml b/morpheus_fornitori/data/supplier_request_stage_data.xml new file mode 100644 index 0000000..9fd4e77 --- /dev/null +++ b/morpheus_fornitori/data/supplier_request_stage_data.xml @@ -0,0 +1,77 @@ + + + + + + + Bozza + 1 + 0 + Richiesta in fase di preparazione, non ancora inviata. + False + False + False + + + + Inviata + 2 + 1 + Richiesta inviata al fornitore, in attesa di risposta. + False + False + False + + + + In approvazione + 3 + 3 + Richiesta in fase di approvazione interna. + False + False + False + + + + Approvata + 4 + 5 + Richiesta approvata, pronta per l'esecuzione. + False + False + False + + + + Completata + 5 + 10 + Richiesta completata con successo. + True + True + False + + + + Rifiutata + 6 + 9 + Richiesta rifiutata dal fornitore o internamente. + True + True + True + + + + Annullata + 7 + 8 + Richiesta annullata prima del completamento. + True + True + True + + + + \ No newline at end of file diff --git a/morpheus_fornitori/models/__init__.py b/morpheus_fornitori/models/__init__.py new file mode 100644 index 0000000..7b7d732 --- /dev/null +++ b/morpheus_fornitori/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import supplier_request_stage +from . import supplier_request \ No newline at end of file diff --git a/morpheus_fornitori/models/supplier_request.py b/morpheus_fornitori/models/supplier_request.py new file mode 100644 index 0000000..0625b54 --- /dev/null +++ b/morpheus_fornitori/models/supplier_request.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api +from odoo.exceptions import UserError + + +class SupplierRequest(models.Model): + _name = 'supplier.request' + _description = 'Richiesta fornitore' + _inherit = ['mail.thread', 'mail.activity.mixin'] + _order = 'create_date desc' + + name = fields.Char( + string="Numero richiesta", + required=True, + copy=False, + readonly=True, + index=True, + default=lambda self: self.env['ir.sequence'].next_by_code('supplier.request') + ) + + # Campo 1: Fornitore (selezionabile dai contatti) + partner_id = fields.Many2one( + 'res.partner', + string="Fornitore", + required=True, + + help="Seleziona il fornitore dalla lista dei contatti" + ) + + # Campo 2: Descrizione + description = fields.Text( + string="Descrizione", + required=True, + help="Descrivi dettagliatamente la richiesta" + ) + + # Campo 3: Allegati + attachment_ids = fields.Many2many( + 'ir.attachment', + 'supplier_request_attachment_rel', + 'request_id', + 'attachment_id', + string="Allegati" + ) + + # Stage configurabile (simile al CRM) + stage_id = fields.Many2one( + 'supplier.request.stage', + string='Stato', + index=True, + tracking=True, + group_expand='_read_group_stage_ids', + ondelete='restrict', + default=lambda self: self.env['supplier.request.stage'].search([('sequence', '=', 1)], limit=1), + help="Stato attuale della richiesta fornitore" + ) + + # Campi informativi + create_uid = fields.Many2one('res.users', string='Creato da', readonly=True) + create_date = fields.Datetime(string='Data Creazione', readonly=True) + + @api.model + def create(self, vals): + if vals.get('name', '/') == '/': + vals['name'] = self.env['ir.sequence'].next_by_code('supplier.request') or '/' + return super(SupplierRequest, self).create(vals) + + @api.model + def _read_group_stage_ids(self, stages, domain): + """Read group customization for stage_id field to always show all stages""" + # Mostra sempre tutti gli stage attivi + search_domain = ['|', ('id', 'in', stages.ids), ('active', '=', True)] + stage_ids = stages.sudo()._search(search_domain, order=stages._order) + return stages.browse(stage_ids) + + def action_move_to_stage(self, stage_name): + """Metodo generico per spostare a uno stage specifico""" + self.ensure_one() + stage = self.env['supplier.request.stage'].search([('name', '=', stage_name)], limit=1) + if stage: + old_stage_name = self.stage_id.name if self.stage_id else 'Nessuno' + self.stage_id = stage + self.message_post(body=f"Richiesta spostata da '{old_stage_name}' a '{stage_name}'.") + else: + raise UserError(f"Stage '{stage_name}' non trovato.") + + def action_submit(self): + """Invia la richiesta""" + self.action_move_to_stage('Inviata') + + def action_approve(self): + """Approva la richiesta""" + self.action_move_to_stage('Approvata') + + def action_reject(self): + """Rifiuta la richiesta""" + self.action_move_to_stage('Rifiutata') + + def action_done(self): + """Completa la richiesta""" + self.action_move_to_stage('Completata') + + def action_cancel(self): + """Annulla la richiesta""" + self.action_move_to_stage('Annullata') + + def action_reset_to_draft(self): + """Ripristina la richiesta in bozza""" + self.action_move_to_stage('Bozza') + + # Metodi di utilità per verificare lo stato + @api.depends('stage_id') + def _compute_is_closed(self): + for record in self: + record.is_closed = record.stage_id.is_closed if record.stage_id else False + + @api.depends('stage_id') + def _compute_is_cancelled(self): + for record in self: + record.is_cancelled = record.stage_id.is_cancelled if record.stage_id else False + + is_closed = fields.Boolean('È chiusa', compute='_compute_is_closed', store=True) + is_cancelled = fields.Boolean('È annullata', compute='_compute_is_cancelled', store=True) \ No newline at end of file diff --git a/morpheus_fornitori/models/supplier_request_stage.py b/morpheus_fornitori/models/supplier_request_stage.py new file mode 100644 index 0000000..a61f198 --- /dev/null +++ b/morpheus_fornitori/models/supplier_request_stage.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models + + +class SupplierRequestStage(models.Model): + """Modello per gli stati delle richieste fornitori. + Simile a crm.stage, permette la configurazione dinamica degli stati. + """ + _name = "supplier.request.stage" + _description = "Supplier Request Stages" + _rec_name = 'name' + _order = "sequence, name, id" + + name = fields.Char('Nome stato', required=True, translate=True) + sequence = fields.Integer('Sequenza', default=1, help="Usato per ordinare gli stati. Valori più bassi vengono mostrati prima.") + description = fields.Text('Descrizione', help="Descrizione interna dello stato per aiutare gli utenti.") + fold = fields.Boolean('Nascosto in kanban', + help='Questo stato è nascosto nella vista kanban quando non ci sono record in quello stato.') + is_closed = fields.Boolean('Stato chiuso', + help='Indica se questo stato rappresenta una richiesta chiusa/completata.') + is_cancelled = fields.Boolean('Stato annullato', + help='Indica se questo stato rappresenta una richiesta annullata.') + + # Colore per la vista kanban + color = fields.Integer('Colore', default=0) + + # Campi per report e analisi + active = fields.Boolean('Attivo', default=True) + + def _default_stages(self): + """Crea gli stati di default se non esistono""" + return [ + {'name': 'Bozza', 'sequence': 1, 'color': 0}, + {'name': 'Inviata', 'sequence': 2, 'color': 1}, + {'name': 'In approvazione', 'sequence': 3, 'color': 3}, + {'name': 'Approvata', 'sequence': 4, 'color': 5}, + {'name': 'Completata', 'sequence': 5, 'color': 10, 'is_closed': True}, + {'name': 'Rifiutata', 'sequence': 6, 'color': 9, 'is_cancelled': True}, + {'name': 'Annullata', 'sequence': 7, 'color': 8, 'is_cancelled': True}, + ] \ No newline at end of file diff --git a/morpheus_fornitori/security/ir.model.access.csv b/morpheus_fornitori/security/ir.model.access.csv new file mode 100644 index 0000000..9fa336d --- /dev/null +++ b/morpheus_fornitori/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_supplier_request_user,Supplier Request User,model_supplier_request,base.group_user,1,1,1,1 +access_supplier_request_manager,Supplier Request Manager,model_supplier_request,base.group_system,1,1,1,1 +access_supplier_request_stage_user,Supplier Request Stage User,model_supplier_request_stage,base.group_user,1,0,0,0 +access_supplier_request_stage_manager,Supplier Request Stage Manager,model_supplier_request_stage,base.group_system,1,1,1,1 \ No newline at end of file diff --git a/morpheus_fornitori/static/description/index.html b/morpheus_fornitori/static/description/index.html new file mode 100644 index 0000000..29de50b --- /dev/null +++ b/morpheus_fornitori/static/description/index.html @@ -0,0 +1,25 @@ + + + + + Morpheus Fornitori + + + Morpheus Fornitori + Modulo semplificato per la gestione delle richieste fornitori. + + Caratteristiche + + Interfaccia semplice con solo 3 campi principali + Selezione fornitore dai contatti esistenti + Campo descrizione per dettagli della richiesta + Gestione allegati + Workflow configurabile con stati personalizzabili + Nessuna logica email automatica + Nessun riferimento a sedi o amministratori + + + Utilizzo + Naviga in "Morpheus Fornitori" → "Richieste Fornitori" per creare e gestire le richieste. + + \ No newline at end of file diff --git a/morpheus_fornitori/views/menus.xml b/morpheus_fornitori/views/menus.xml new file mode 100644 index 0000000..a9d14ee --- /dev/null +++ b/morpheus_fornitori/views/menus.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/morpheus_fornitori/views/menus_prodotti.xml b/morpheus_fornitori/views/menus_prodotti.xml new file mode 100644 index 0000000..fe95acf --- /dev/null +++ b/morpheus_fornitori/views/menus_prodotti.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/morpheus_fornitori/views/supplier_request_stage_views.xml b/morpheus_fornitori/views/supplier_request_stage_views.xml new file mode 100644 index 0000000..acce01b --- /dev/null +++ b/morpheus_fornitori/views/supplier_request_stage_views.xml @@ -0,0 +1,89 @@ + + + + + + supplier.request.stage.search + supplier.request.stage + + + + + + + + + + + + + + + + + supplier.request.stage.list + supplier.request.stage + + + + + + + + + + + + + + + + + supplier.request.stage.form + supplier.request.stage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Stati richieste + supplier.request.stage + list,form + + + + Configura un nuovo stato per le richieste fornitori + + + Gli stati permettono di organizzare il flusso di lavoro delle richieste fornitori. + Puoi creare stati personalizzati, riordinarli e configurare le loro proprietà. + + + + + \ No newline at end of file diff --git a/morpheus_fornitori/views/supplier_request_views.xml b/morpheus_fornitori/views/supplier_request_views.xml new file mode 100644 index 0000000..c6ddfb4 --- /dev/null +++ b/morpheus_fornitori/views/supplier_request_views.xml @@ -0,0 +1,166 @@ + + + + + supplier.request.form + supplier.request + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + supplier.request.tree + supplier.request + + + + + + + + + + + + + + + supplier.request.search + supplier.request + + + + + + + + + + + + + + + + + + + + + + + + supplier.request.kanban + supplier.request + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Richieste Fornitori + supplier.request + kanban,list,form + + + + Nessuna richiesta fornitore trovata. + + + Crea la tua prima richiesta fornitore cliccando su "Nuovo". + + + + \ No newline at end of file
Modulo semplificato per la gestione delle richieste fornitori.
Naviga in "Morpheus Fornitori" → "Richieste Fornitori" per creare e gestire le richieste.
+ Configura un nuovo stato per le richieste fornitori +
+ Gli stati permettono di organizzare il flusso di lavoro delle richieste fornitori. + Puoi creare stati personalizzati, riordinarli e configurare le loro proprietà. +
+ Nessuna richiesta fornitore trovata. +
+ Crea la tua prima richiesta fornitore cliccando su "Nuovo". +