from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError, Warning from psycopg2 import sql, DatabaseError from dateutil.relativedelta import relativedelta from datetime import datetime from werkzeug import utils class EventRegistration(models.Model): _inherit = 'event.registration' room_id = fields.Many2one( 'booking.room', String='Room', index=True, group_expand='_read_group_room_ids', track_visibility='onchange', ) booking_event=fields.Boolean(related='event_id.booking_event') event_end=fields.Boolean(related='event_id.stage_id.pipe_end') medical_concern=fields.Selection( [('have medical concern','I have a medical concern to report'), ('have no medical concern','I have no medical concern to report'), ('no answer','I don\'t want to answer')], string='Medical concern', ) medical_information=fields.Char('Medical informations') medical_contact_name=fields.Char('Name and Firstname of the contact') medical_contact_phone=fields.Char('Phone of the contact') questionnaire_ids=fields.One2many( 'event.registration_questionnaire', 'event_registration_id', String='Questionnaire', readonly=True ) option_ids=fields.One2many( 'event.registration_option', 'event_registration_id', String='Options', readonly=True ) donation_ids=fields.One2many( 'donation.donation', 'event_registration_id', String='Donations', readonly=True ) @api.model def _default_currency(self): company = self.env['res.company']._company_default_get( 'event.registration') return company.currency_id currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, track_visibility='onchange', ondelete='restrict', default=_default_currency ) down_payment=fields.Boolean('down payment',default=False) firstname=fields.Char('Firstname') age=fields.Integer('Age') gender=fields.Selection([('male','Male'),('femelle','Femelle')],string='gender') mobile=fields.Char('Mobile') kanban_color=fields.Char('kanban color',compute='_compute_kanban_color') invoice_state=fields.Selection(related='invoice_id.payment_state') down_payment_invoice_state=fields.Selection(related='down_payment_invoice_id.payment_state') balance_invoice_state=fields.Selection(related='balance_invoice_id.payment_state') invoice_id=fields.Many2one('account.move', readonly=True, ondelete="cascade") #invoice_state=fields.Selection(related='invoice_id.state') down_payment_invoice_id=fields.Many2one('account.move', ondelete="cascade") #down_payment_invoice_state=fields.Selection(related='down_payment_order_id.state') balance_invoice_id=fields.Many2one('account.move' ,ondelete="cascade") #balance_invoice_state=fields.Selection(related='balance_order_id.state') payment_status=fields.Char(string='payment status',compute='_compute_payment_status') def _compute_payment_status(self): for rec in self: rec.payment_status='not paid' if rec.invoice_id: if rec.invoice_state not in ('paid'):rec.payment_status='not paid' if rec.invoice_state=='paid':rec.payment_status='paid' else: if rec.down_payment_invoice_id and balance_invoice_id: if rec.down_payment_invoice_state!='paid':rec.payment_status='down payment not paid' if rec.down_payment_invoice_state=='paid' and rec.balance_payment_invoice_state=='paid' :rec.payment_status='paid' if rec.down_payment_invoice_state=='paid' and rec.balance_payment_invoice_state!='paid':rec.payment_status='down payment paid' def _compute_kanban_color(self): for rec in self: rec.kanban_color='' if rec.gender=='male':rec.kanban_color="d0e0e3" if rec.gender=='femelle':rec.kanban_color="eebbcc" @api.model def _read_group_room_ids(self, stages, domain, order): return self.env['booking.room'].search([]) @api.model_create_multi def create(self, vals_list): registrations = super(EventRegistration, self).create(vals_list) if registrations._check_auto_confirmation(): registrations.sudo().action_confirm() #ajout du questionnaire pour la personne inscrite questions=self.env['event.question'].search([('event_id','=',int(registrations.event_id))]) if questions: for question in questions: vals={} vals['sequence']=question.sequence vals['question']=question.question vals['event_registration_id']=registrations.id res=self.env['event.registration_questionnaire'].create(vals) #ajout des options pour la personne inscrite options=self.env['booking.option'].search([('event_id','=',int(registrations.event_id))]) if options: for option in options: vals={} vals['booking_option_id']=int(option.booking_option_id) vals['booking_option_price']=option.booking_option_price vals['event_registration_id']=int(registrations.id) res=self.env['event.registration_option'].create(vals) return registrations def action_event_registration_order(self): return True def create_donation(self,event_registration_id,partner_id,product_id,price_unit): vals={} vals['partner_id']=int(partner_id) #raise Warning(vals['partner_id']) vals['donation_date']=datetime.now() vals['tax_receipt_option']='annual' #mode de paiement CB par defaut electronic_method=self.env['account.payment.method'].search([('code','=','electronic')],limit=1) if electronic_method: cb_mode=self.env['account.payment.mode'].search([('payment_method_id','=',int(electronic_method.id))],limit=1) if cb_mode: vals['payment_mode_id']=cb_mode.id else: raise Warning('please configure credit card mode') #tant que le devis n'est pas validé (paiment effectué), le don est en brouillon vals['state']='draft' vals['payment_ref']='internet' donation_draft=self.env['donation.donation'].sudo().create(vals) #ajout du don au niveau de l'inscription reg=self.env['event.registration'].search([('id','=',int(event_registration_id))]) if reg: reg.write({'donation_ids':[(4,int(donation_draft.id))]}) vals={} #create line donation vals['donation_id']=donation_draft.id product=self.env['product.product'].search([('id','=',int(product_id))]) vals['product_id']=int(product_id) vals['display_name']=product.name vals['quantity']=1 vals['unit_price']=price_unit vals['tax_receipt_ok']=product.tax_receipt_ok donation_line=self.env['donation.line'].sudo().create(vals) donation_draft.state='done' def action_event_registration_generate_order(self,id_registration=None): #suppression des devis existants if self.order_id: self.env['sale.order'].search([('id','=',int(self.order_id))]).unlink() self.order_id=False if self.down_payment_order_id: self.env['sale.order'].search([('id','=',int(self.down_payment_order_id))]).unlink() self.down_payment_order_id=False if self.balance_order_id: self.env['sale.order'].search([('id','=',int(self.balance_order_id))]).unlink() self.balance_order_id=False #suppression des dons existants self.env['donation.donation'].search([('id','in',self.donation_ids)]).unlink() self.donation_ids=False if id_registration:reg=self.env['event.registration'].search([('id','=',int(id_registration))]) else : reg=self event=self.env['event.event'].search([('id','=',int(reg.event_id))]) selected_registrant_options=self.env['event.registration_option'].search([ ('event_registration_id','=',int(reg.id))]) #controles avant traitement : un produit doit être associé à l'événement if not event.booking_product_id: raise Warning('product missing for event booking') #Prix à appliquer au produit en fonction du statut de l'inscrit status=reg.partner_id.member_status product_price=event.booking_price if status=='not member':product_price=event.booking_price if status=='member':product_price=event.booking_member_price if status=='super member':product_price=event.booking_super_member_price #Prix à appliquer au produit si paiement de l'adhésion membership_option=False membership_product=self.env['event.membership_product'].search([]) if membership_product: if selected_registrant_options: for opt in selected_registrant_options: if opt.booking_option_id==membership_product.id: membership_option=True if status!="super member": product_price=event.booking_member_price #calcul du montant total à régler #création du devis sans accompte if not reg.down_payment: #création du devis vals={} vals['partner_id']=int(reg.partner_id) order=self.env['sale.order'].create(vals) #création des lignes de devis vals={} vals['order_id']=order.id #ajout du produit vals['product_id']=int(event.booking_product_id) vals['product_uom_qty']=1 vals['price_unit']=product_price vals['name']=event.booking_product_id.name order_line=self.env['sale.order.line'].create(vals) #ajout des options vals={} if selected_registrant_options: for option in selected_registrant_options: vals['order_id']=order.id vals['product_id']=int(option.booking_option_id.id) vals['product_uom_qty']=1 prd=self.env['product.product'].search([('id','=',int(option.booking_option_id.id))]) vals['name']=prd.name #prix à appliquer aux options en fonction du statut ou de la présence de l'option d'adhesion event_option=self.env['booking.option'].search(['&',('event_id','=',int(reg.event_id)),('booking_option_id','=',int(option.booking_option_id.id))]) if event_option: if status=="super member":vals['price_unit']=event_option.booking_option_super_member_price if status=="member":vals['price_unit']=event_option.booking_option_member_price if status=="not member" and membership_option: vals['price_unit']=event_option.booking_option_member_price if status=="not member" and not membership_option: vals['price_unit']=event_option.booking_option_price order_line=self.env['sale.order.line'].create(vals) #si l'option est un produit de don on créé un don pour la personne #if prd.donation: self.create_donation(reg.id,reg.partner_id,vals['product_id'],vals['price_unit']) order.message_subscribe(partner_ids=order.partner_id.ids) order.state='sent' reg.order_id=order.id return order.id #2 devis si paiement avec acompte else: #création du 1er devis d'acompte vals={} vals['partner_id']=int(reg.partner_id) order=self.env['sale.order'].create(vals) #création des lignes de devis vals={} vals['order_id']=order.id #ajout du produit vals['product_id']=int(event.booking_product_id) vals['product_uom_qty']=1 vals['price_unit']=event.booking_down_payment vals['name']='Acompte '+event.booking_product_id.name order_line=self.env['sale.order.line'].create(vals) order.message_subscribe(partner_ids=order.partner_id.ids) order.state='sent' reg.down_payment_order_id=order.id #création du 2ème devis de solde + paiement des options vals={} vals['partner_id']=int(reg.partner_id) order2=self.env['sale.order'].create(vals) #création des lignes de devis vals={} vals['order_id']=order2.id #ajout du produit vals['product_id']=int(event.booking_product_id) vals['product_uom_qty']=1 vals['price_unit']=product_price-event.booking_down_payment vals['name']='solde '+event.booking_product_id.name order_line=self.env['sale.order.line'].create(vals) #ajout des options vals={} if selected_registrant_options: for option in selected_registrant_options: vals['order_id']=order2.id vals['product_id']=int(option.booking_option_id.id) vals['product_uom_qty']=1 prd=self.env['product.product'].search([('id','=',int(option.booking_option_id.id))]) vals['name']=prd.name #prix à appliquer aux options en fonction du statut ou de la présence de l'option d'adhesion event_option=self.env['booking.option'].search(['&',('event_id','=',int(reg.event_id)),('booking_option_id','=',int(option.booking_option_id.id))]) if event_option: if status=="super member":vals['price_unit']=event_option.booking_option_super_member_price if status=="member":vals['price_unit']=event_option.booking_option_member_price if status=="not member" and membership_option: vals['price_unit']=event_option.booking_option_member_price if status=="not member" and not membership_option: vals['price_unit']=event_option.booking_option_price order_line=self.env['sale.order.line'].create(vals) order2.message_subscribe(partner_ids=order2.partner_id.ids) order2.state='sent' reg.balance_order_id=order2.id return order.id def action_event_registration_generate_invoice(self,id_registration=None): #suppression des factures existantes # if self.invoice_id: # self.env['account.move'].search([('id','=',int(self.invoice_id))]).unlink() # self.invoice_id=False # if self.down_payment_invoice_id: # self.env['account.move'].search([('id','=',int(self.down_payment_invoice_id))]).unlink() # self.down_payment_invoice_id=False # if self.balance_invoice_id: # self.env['account.move'].search([('id','=',int(self.balance_invoice_id))]).unlink() # self.balance_invoice_id=False #suppression des dons existants self.env['donation.donation'].search([('id','in',self.donation_ids)]).unlink() self.donation_ids=False if id_registration:reg=self.env['event.registration'].search([('id','=',int(id_registration))]) else : reg=self event=self.env['event.event'].search([('id','=',int(reg.event_id))]) selected_registrant_options=self.env['event.registration_option'].search([ ('event_registration_id','=',int(reg.id))]) #controles avant traitement : un produit doit être associé à l'événement if not event.booking_product_id: raise Warning('product missing for event booking') #Prix à appliquer au produit en fonction du statut de l'inscrit status=reg.partner_id.member_status product_price=event.booking_price if status=='not member':product_price=event.booking_price if status=='member':product_price=event.booking_member_price if status=='super member':product_price=event.booking_super_member_price #Prix à appliquer au produit si paiement de l'adhésion membership_option=False #membership_product=self.env['event.membership_product'].search([]) membership_product=self.env['product.product'].sudo().search([('membership_product','=',True)],limit=1) if membership_product: if selected_registrant_options: for opt in selected_registrant_options: if opt.booking_option_id==membership_product.id: membership_option=True if status!="super member": product_price=event.booking_member_price #calcul du montant total à régler #création du devis sans accompte if not reg.down_payment: #création de la facture vals={} vals['partner_id']=int(reg.partner_id) vals['invoice_date']=datetime.now() #mode de paiement CB par defaut electronic_method=self.env['account.payment.method'].search([('code','=','electronic')],limit=1) if electronic_method: cb_mode=self.env['account.payment.mode'].search([('payment_method_id','=',int(electronic_method.id))],limit=1) if cb_mode: vals['payment_mode_id']=cb_mode.id else: raise Warning('please configure credit card mode') vals['move_type']='out_invoice' vals['state']='draft' #vals['currency_id']=self.currency_id.id invoice=self.env['account.move'].create(vals) invoice.state='posted' invoice.name='REC'+str(invoice.id) invoice.payment_reference=invoice.name vals={} account_credit=self.env['account.account'].search([('code','=','707100')]) account_debit=self.env['account.account'].search([('code','=','411100')]) vals['move_id']=invoice.id vals['product_id']=int(event.booking_product_id) vals['quantity']=1 vals['price_unit']=product_price vals['name']=event.booking_product_id.name vals['account_id']=int(account_credit.id) invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals) # #debit_line vals_d={} vals_d['move_id']=invoice.id vals_d['debit']=product_price vals_d['credit']=0 vals_d['date']=datetime.now() vals_d['partner_id']=int(reg.partner_id) vals_d['product_id']=int(event.booking_product_id) vals_d['name']=event.booking_product_id.name vals_d['account_id']=int(account_debit.id) vals_d['quantity']=1 vals_d['price_unit']=product_price vals_d['exclude_from_invoice_tab']=True invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals_d) l=self.env['account.move.line'].search([('move_id','=',invoice.id),('balance','<',0)]) l.partner_id=int(reg.partner_id) #ajout des options if selected_registrant_options: for option in selected_registrant_options: prd=self.env['product.product'].search([('id','=',int(option.booking_option_id.id))]) #prix à appliquer aux options en fonction du statut ou de la présence de l'option d'adhesion event_option=self.env['booking.option'].search(['&',('event_id','=',int(reg.event_id)),('booking_option_id','=',int(option.booking_option_id.id))],limit=1) if event_option: if status=="super member":price_unit=event_option.booking_option_super_member_price if status=="member":price_unit=event_option.booking_option_member_price if status=="not member" and membership_option:price_unit=event_option.booking_option_member_price if status=="not member" and not membership_option:price_unit=event_option.booking_option_price vals={} vals['move_id']=invoice.id #ajout du produit vals['product_id']=int(event.booking_product_id) vals['quantity']=1 vals['price_unit']=price_unit vals['name']=prd.name vals['account_id']=int(account_credit.id) invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals) #debit_line vals_d={} vals_d['move_id']=invoice.id vals_d['debit']=price_unit vals_d['credit']=0 vals_d['date']=datetime.now() vals_d['partner_id']=int(reg.partner_id) vals_d['product_id']=int(option.booking_option_id.id) vals_d['name']=prd.name vals_d['account_id']=int(account_debit.id) vals_d['quantity']=1 vals_d['price_unit']=price_unit vals_d['exclude_from_invoice_tab']=True invoice_line=self.env['account.move.line'].with_context(check_move_validity=True).create([vals_d]) l=self.env['account.move.line'].search([('move_id','=',invoice.id),('balance','<',0)]) l.partner_id=int(reg.partner_id) #si l'option est un produit de don on créé un don pour la personne if prd.donation: self.create_donation(reg.id,reg.partner_id,vals['product_id'],vals['price_unit']) reg.invoice_id=invoice.id return invoice #2 factures si paiement avec acompte else: #création de la 1ère facture d'acompte vals={} vals['partner_id']=int(reg.partner_id) vals['invoice_date']=datetime.now() #mode de paiement CB par defaut electronic_method=self.env['account.payment.method'].search([('code','=','electronic')],limit=1) if electronic_method: cb_mode=self.env['account.payment.mode'].search([('payment_method_id','=',int(electronic_method.id))],limit=1) if cb_mode: vals['payment_mode_id']=cb_mode.id else: raise Warning('please configure credit card mode') vals['move_type']='out_invoice' vals['state']='draft' #vals['currency_id']=self.currency_id.id invoice=self.env['account.move'].create(vals) invoice.state='posted' invoice.name='REC'+str(invoice.id) invoice.payment_reference=invoice.name vals={} account_credit=self.env['account.account'].search([('code','=','707100')]) account_debit=self.env['account.account'].search([('code','=','411100')]) vals['move_id']=invoice.id vals['product_id']=int(event.booking_product_id) vals['quantity']=1 vals['price_unit']=event.booking_down_payment vals['name']='Acompte '+event.booking_product_id.name vals['account_id']=int(account_credit.id) invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals) # #debit_line vals_d={} vals_d['move_id']=invoice.id vals_d['debit']=event.booking_down_payment vals_d['credit']=0 vals_d['date']=datetime.now() vals_d['partner_id']=int(reg.partner_id) vals_d['product_id']=int(event.booking_product_id) vals_d['name']='Acompte '+event.booking_product_id.name vals_d['account_id']=int(account_debit.id) vals_d['quantity']=1 vals_d['price_unit']=event.booking_down_payment vals_d['exclude_from_invoice_tab']=True invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals_d) l=self.env['account.move.line'].search([('move_id','=',invoice.id),('balance','<',0)]) l.partner_id=int(reg.partner_id) reg.down_payment_invoice_id=invoice.id #création 2ème facture de solde + paiement des options vals={} vals['partner_id']=int(reg.partner_id) vals['invoice_date']=datetime.now() vals['move_type']='out_invoice' vals['state']='draft' #vals['currency_id']=self.currency_id.id invoice=self.env['account.move'].create(vals) invoice.state='posted' invoice.name='REC'+str(invoice.id) invoice.payment_reference=invoice.name vals={} vals['move_id']=invoice.id vals['product_id']=int(event.booking_product_id) vals['quantity']=1 vals['price_unit']=product_price-event.booking_down_payment vals['name']='solde '+event.booking_product_id.name vals['account_id']=int(account_credit.id) invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals) # #debit_line vals_d={} vals_d['move_id']=invoice.id vals_d['debit']=product_price-event.booking_down_payment vals_d['credit']=0 vals_d['date']=datetime.now() vals_d['partner_id']=int(reg.partner_id) vals_d['product_id']=int(event.booking_product_id) vals_d['name']='solde '+event.booking_product_id.name vals_d['account_id']=int(account_debit.id) vals_d['quantity']=1 vals_d['price_unit']=product_price-event.booking_down_payment vals_d['exclude_from_invoice_tab']=True invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals_d) l=self.env['account.move.line'].search([('move_id','=',invoice.id),('balance','<',0)]) l.partner_id=int(reg.partner_id) #ajout des options if selected_registrant_options: for option in selected_registrant_options: prd=self.env['product.product'].search([('id','=',int(option.booking_option_id.id))]) #prix à appliquer aux options en fonction du statut ou de la présence de l'option d'adhesion event_option=self.env['booking.option'].search(['&',('event_id','=',int(reg.event_id)),('booking_option_id','=',int(option.booking_option_id.id))]) if event_option: if status=="super member":price_unit=event_option.booking_option_super_member_price if status=="member":price_unit=event_option.booking_option_member_price if status=="not member" and membership_option:price_unit=event_option.booking_option_member_price if status=="not member" and not membership_option:price_unit=event_option.booking_option_price vals={} vals['move_id']=invoice.id #ajout du produit vals['product_id']=int(event.booking_product_id) vals['quantity']=1 vals['price_unit']=price_unit vals['name']=prd.name vals['account_id']=int(account_credit.id) invoice_line=self.env['account.move.line'].with_context(check_move_validity=False).create(vals) #debit_line vals_d={} vals_d['move_id']=invoice.id vals_d['debit']=price_unit vals_d['credit']=0 vals_d['date']=datetime.now() vals_d['partner_id']=int(reg.partner_id) vals_d['product_id']=int(option.booking_option_id.id) vals_d['name']=prd.name vals_d['account_id']=int(account_debit.id) vals_d['quantity']=1 vals_d['price_unit']=price_unit vals_d['exclude_from_invoice_tab']=True invoice_line=self.env['account.move.line'].with_context(check_move_validity=True).create([vals_d]) l=self.env['account.move.line'].search([('move_id','=',invoice.id),('balance','<',0)]) l.partner_id=int(reg.partner_id) #si l'option est un produit de don on créé un don pour la personne if prd.donation: self.create_donation(reg.id,reg.partner_id,vals['product_id'],vals['price_unit']) reg.balance_invoice_id=invoice.id return invoice.id def info_objet(self,model_id,objet_id): field_list=request.env[model_id].sudo().fields_get() result=[] for key in field_list: result.append((key,key)) result.sort() return result def create_payment(self,invoice_id,code_payment_method): invoice=self.env['account.move'].search([('id','=',int(invoice_id))]) payment_method=self.env['account.payment.method'].search([('code','=',code_payment_method),('payment_type','=','inbound')]) bank=self.env['account.journal'].search([('type','=','bank')]) if not payment_method : raise Warning ('please set up the '+code_payment_method+ ' method') if not bank : raise Warning ('please set up the bank journal !') Payment = self.env['account.payment'].with_context(default_invoice_ids=[(4, invoice_id, False)]) payment = Payment.create({ 'date': datetime.now(), 'payment_method_id': payment_method.id, 'payment_type': 'inbound', 'partner_type': 'customer', 'partner_id': invoice.partner_id.id, 'amount': invoice.amount_residual, 'journal_id': int(bank.id), 'company_id': self.env.company, 'currency_id': int(self.currency_id) #'payment_difference_handling': 'reconcile', #'writeoff_account_id': self.diff_income_account.id, }) payment.state='posted' invoice.payment_id=payment.id invoice.payment_order_ok=True class EventRegistration_questionnaire(models.Model): _name = 'event.registration_questionnaire' _description = 'event registration questionnaire' sequence = fields.Integer(string="sequence", default=10) question=fields.Text(string='question',readonly=True) answer=fields.Text(string='answer') event_registration_id=fields.Many2one( 'event.registration', String='Event registration', index=True, readonly=True, track_visibility='onchange', ondelete='cascade' ) class EventRegistration_option(models.Model): _name = 'event.registration_option' _description = 'event registration option' booking_option_id=fields.Many2one('product.product',string='booking options',domain="[('id','in',selectable_option_ids)]") booking_option_price=fields.Monetary('Price',currency_field='currency_id',readonly=True) selectable_option_ids = fields.Many2many('product.product', compute='_compute_selectable_option') event_registration_id=fields.Many2one( 'event.registration', String='Event registration', index=True, readonly=True, track_visibility='onchange', ) def _compute_selectable_option(self): selectable_options=self.env['booking.option'].search([('event_id','=',int(self.event_registration_id.event_id))]) if selectable_options: self.selectable_option_ids=selectable_options.booking_option_id.ids @api.model def _default_currency(self): company = self.env['res.company']._company_default_get( 'event.event') return company.currency_id currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, states={'done': [('readonly', True)]}, track_visibility='onchange', ondelete='restrict', default=_default_currency )