# -*- coding: utf-8 -*- ################################################################################# # Author : Webkul Software Pvt. Ltd. (:wink: # Copyright(c): 2015-Present Webkul Software Pvt. Ltd. # All Rights Reserved. # # # # This program is copyright property of the author mentioned above. # You can`t redistribute it and/or modify it. # # # You should have received a copy of the License along with this program. # If not, see ; ################################################################################# from odoo import models, fields, api, _ import datetime from datetime import timedelta from odoo.exceptions import UserError import logging _logger = logging.getLogger(__name__) class AffiliateVisit(models.Model): _name = "affiliate.visit" _order = "create_date desc" _description = "Affiliate Visit Model" name = fields.Char(string="Name", readonly='True') # @api.multi @api.depends('affiliate_type', 'type_id') def _calc_type_name(self): for record in self: if record.affiliate_type == 'product': record.type_name = record.env['product.template'].browse([record.type_id]).name if record.affiliate_type == 'category': record.type_name = record.env['product.public.category'].browse([record.type_id]).name affiliate_method = fields.Selection([("ppc", "Pay Per Click"), ("pps", "Pay Per Sale")], string="Order Report", readonly='True', states={'draft': [('readonly', False)]}, help="state of traffic either ppc or pps") affiliate_type = fields.Selection([("product", "Product"), ("category", "Category")], string="Affiliate Type", readonly='True', states={'draft': [('readonly', False)]}, help="whether the ppc or pps is on product or category") type_id = fields.Integer(string='Type Id', readonly='True', states={'draft': [('readonly', False)]}, help="Id of product template on which ppc or pps traffic create") type_name = fields.Char(string='Type Name', readonly='True', states={'draft': [('readonly', False)]}, compute='_calc_type_name', help="Name of the product") is_converted = fields.Boolean(string="Is Converted", readonly='True', states={'draft': [('readonly', False)]}) sales_order_line_id = fields.Many2one("sale.order.line", readonly='True', states={'draft': [('readonly', False)]}) affiliate_key = fields.Char(string="Key", readonly='True', states={'draft': [('readonly', False)]}) affiliate_partner_id = fields.Many2one("res.partner", string="Affiliate", readonly='True', states={'draft': [('readonly', False)]}) url = fields.Char(string="Url", readonly='True', states={'draft': [('readonly', False)]}) ip_address = fields.Char(readonly='True', states={'draft': [('readonly', False)]}) currency_id = fields.Many2one('res.currency', 'Currency', required=True, default=lambda self: self.env.user.company_id.currency_id.id, readonly='True', states={'draft': [('readonly', False)]}) convert_date = fields.Datetime(string='Date', readonly='True', states={'draft': [('readonly', False)]}) price_total = fields.Monetary(string="Sale value", related='sales_order_line_id.price_total', states={'draft': [('readonly', False)]}, help="Total sale value of product") unit_price = fields.Float(string="Product Unit Price", related='sales_order_line_id.price_unit', readonly='True', states={'draft': [('readonly', False)]}, help="price unit of the product") commission_amt = fields.Float(readonly='True', states={'draft': [('readonly', False)]}) affiliate_program_id = fields.Many2one('affiliate.program', string='Program', readonly='True', states={'draft': [('readonly', False)]}) amt_type = fields.Char(string='Commission Matrix', readonly='True', states={'draft': [('readonly', False)]}, help="Commission Matrix on which commission value calculated") act_invoice_id = fields.Many2one("account.move", string='Act Invoice id', readonly='True', states={'draft': [('readonly', False)]}) state = fields.Selection([ ('draft', 'Draft'), ('cancel', 'Cancel'), ('confirm', 'Confirm'), ('invoice', 'Invoiced'), ('paid', 'Paid'), ], string='Status', readonly=True, default='draft') product_quantity = fields.Integer(readonly='True', states={'draft': [('readonly', False)]}) @api.model_create_multi def create(self, vals_list): new_visit = None for vals in vals_list: vals['name'] = self.env['ir.sequence'].next_by_code('affiliate.visit') new_visit = super(AffiliateVisit, self).create(vals) return new_visit # button on view action def action_cancel(self): self.state = 'cancel' return True # button on view action def action_confirm(self): check_enable_ppc = self.env['res.config.settings'].sudo().website_constant().get('enable_ppc') if self.affiliate_method != 'ppc' and not self.sales_order_line_id: raise UserError("Order is not present in visit %s." % self.name) if self.affiliate_method != 'ppc' and not self.price_total: raise UserError("Sale value must be greater than zero.") if self.affiliate_method == 'ppc' and (not check_enable_ppc): raise UserError("Pay per click is disable, so you can't confirm it's commission") self.state = 'confirm' status = self._get_rate(self.affiliate_method, self.affiliate_type, self.type_id) if status.get('is_error'): raise UserError(status['message']) return True # button on view action def action_paid(self): self.state = 'paid' return True # scheduler according to the scheduler define in data automated scheduler @api.model def process_scheduler_queue(self): users_all = self.env['res.users'].search([('is_affiliate', '=', True)]) ConfigValues = self.env['res.config.settings'].sudo().website_constant() payment_day = ConfigValues.get('payment_day') threshold_amt = ConfigValues.get('minimum_amt') # make the date of current month of setting date payment_date = datetime.date(datetime.date.today().year, datetime.date.today().month, payment_day) for u in users_all: visits = self.search([('state', '=', 'confirm'), ('affiliate_partner_id', '=', u.partner_id.id)]) if payment_date and visits: visits = visits.filtered(lambda r: fields.Date.from_string(r.create_date) <= payment_date) _logger.info("*****filter- visits=%r******", visits) _logger.info("****before******before method***visits**%r*******", visits) visits = self.check_enable_ppc_visits(visits) # function to filter the visits if ppc is enable or disable accordingly _logger.info("*****after*****after method***visits**%r*******", visits) total_comm_per_user = 0 if visits: for v in visits: total_comm_per_user = total_comm_per_user + v.commission_amt if total_comm_per_user >= threshold_amt and payment_date: dic = { 'name': "Total earn commission on ppc and pps", 'quantity': 1, 'price_unit': total_comm_per_user, 'product_id': ConfigValues.get('aff_product_id'), } invoice_dict = [ { 'invoice_line_ids': [(0, 0, dic)], 'move_type': 'in_invoice', 'partner_id': u.partner_id.id, 'invoice_date': fields.Datetime.now().date() } ] inv_id = self.env['account.move'].create(invoice_dict) for v in visits: v.state = 'invoice' v.act_invoice_id = inv_id.id return True def check_enable_ppc_visits(self, visits): check_enable_ppc = self.env['res.config.settings'].sudo().website_constant().get('enable_ppc') if check_enable_ppc: return visits else: visits = visits.filtered(lambda v: v.affiliate_method == 'pps') return visits # method call from server action @api.model def create_invoice(self): # get the value of enable ppc from settings ConfigValues = self.env['res.config.settings'].sudo().website_constant() check_enable_ppc = ConfigValues.get('enable_ppc') aff_vst = self._context.get('active_ids') act_invoice = self.env['account.move'] # check the first visit of context is ppc or pps and enable pps affiliate_method_type = self.browse([aff_vst[0]]).affiliate_method if affiliate_method_type == 'ppc' and (not check_enable_ppc): raise UserError("Pay per click is disable, so you can't generate it's invoice") invoice_ids = [] for v in aff_vst: vst = self.browse([v]) # [[0, 'virtual_754', {'sequence': 10, 'product_id': 36, 'name': '[Deposit] Deposit', 'account_id': 21, 'analytic_account_id': False, 'analytic_tag_ids': [[6, False, []]], # 'quantity': 1, 'product_uom_id': 1, 'price_unit': 150, 'discount': 0, 'tax_ids': [[6, False, [1]]] if vst.state == 'confirm': # ********** creating invoice line ********************* if vst.sales_order_line_id: dic = { 'name': "Type " + vst.affiliate_type + " on Pay Per Sale ", 'quantity': 1, 'price_unit': vst.commission_amt, # 'move_id':inv_id.id, # 'product_id':ConfigValues.get('aff_product_id'), } else: dic = { 'name': "Type " + vst.affiliate_type + " on Pay Per Click ", 'price_unit': vst.commission_amt, 'quantity': 1, # 'product_id':ConfigValues.get('aff_product_id'), } invoice_dict = [ { 'invoice_line_ids': [(0, 0, dic)], 'move_type': 'in_invoice', 'partner_id': vst.affiliate_partner_id.id, 'invoice_date': fields.Datetime.now().date() }] line = self.env['account.move'].create(invoice_dict) vst.state = 'invoice' vst.act_invoice_id = line.id invoice_ids.append(line) msg = _('%s records has been invoiced out of %s') % (len(invoice_ids), len(aff_vst)) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'type': 'success', 'message': msg, 'next': {'type': 'ir.actions.act_window_close'}, } } def _get_rate(self, affiliate_method, affiliate_type, type_id): # _get_rate() methods arguments (pps,product,product_id) or (ppc,product,product_id) or (ppc,category,category_id) # check product.id in product.template # check category.id in product.public.category product_exists = False category_exists = False commission = 0.0 commission_type = False adv_commision_amount = False from_currency = self.sales_order_line_id.currency_id company = self.env.user.company_id response = {} if self.affiliate_program_id: if affiliate_type == 'product': product_exists = self.env['product.template'].browse([type_id]) if affiliate_type == 'category': category_exists = self.env['product.public.category'].browse([type_id]) if affiliate_method == 'ppc' and product_exists or category_exists: # pay per click commission = from_currency._convert(self.affiliate_program_id.amount_ppc_fixed, self.affiliate_program_id.currency_id, company, fields.Date.today()) commission_type = 'fixed' self.commission_amt = commission else: # pay per sale if affiliate_method == 'pps' and product_exists: # for pps_type simple if self.affiliate_program_id.pps_type == 's': if self.affiliate_program_id.matrix_type == 'f': # fixed amt = from_currency._convert(self.affiliate_program_id.amount, self.affiliate_program_id.currency_id, company, fields.Date.today()) commission = amt * self.product_quantity commission_type = 'fixed' else: if self.affiliate_program_id.matrix_type == 'p' and (not self.affiliate_program_id.amount > 100): # percentage amt_product = from_currency._convert(self.price_total, self.affiliate_program_id.currency_id, company, fields.Date.today()) commission = (amt_product * self.affiliate_program_id.amount / 100) commission_type = 'percentage' else: response = { 'is_error': 1, 'message': 'Percenatge amount is greater than 100', } else: # for pps type advance (advance depends upon price list) if self.affiliate_program_id.pps_type == 'a' and product_exists: # for pps_type advance adv_commision_amount, commission, commission_type = self.advance_pps_type_calc() # adv_commision_amount = is a amount if advance commission # commission = is a amount which is earned by commisiion commission = commission * self.product_quantity _logger.info("----commision_value-%r--------commision_value_type-%r------", commission, commission_type) _logger.info('================advance_pps_type_calc===============') if commission and commission_type: _logger.info("---22----adv_commision_amount--%r--commision_value-%r--------commision_value_type-%r------", adv_commision_amount, commission, commission_type) else: response = { 'is_error': 1, 'message': 'No commission Category Found for this product..' } else: response = { 'is_error': 1, 'message': 'pps_type is advance', } else: response = { 'is_error': 1, 'message': 'Affilite method is niether ppc nor pps or affiliate type is absent(product or category)', } else: response = { 'is_error': 1, 'message': 'Program is absent in visit', } if commission: self.commission_amt = commission # self.amt_type = commission_type if commission_type == 'fixed' and affiliate_method == 'ppc': self.amt_type = self.affiliate_program_id.currency_id.symbol + str(commission) if commission_type == 'percentage' and affiliate_method == 'ppc': self.amt_type = str(self.affiliate_program_id.amount_ppc_fixed) + '%' # for pps if commission_type == 'percentage' and self.affiliate_program_id.pps_type == 's': self.amt_type = str(self.affiliate_program_id.amount) + "%" if commission_type == 'fixed' and affiliate_method == 'pps' and self.affiliate_program_id.pps_type == 's': self.amt_type = self.affiliate_program_id.currency_id.symbol + str(commission) if commission_type == 'fixed' and affiliate_method == 'pps' and self.affiliate_program_id.pps_type == 'a': self.amt_type = self.affiliate_program_id.currency_id.symbol + str(adv_commision_amount) + " advance" if commission_type == 'percentage' and affiliate_method == 'pps' and self.affiliate_program_id.pps_type == 'a': self.amt_type = str(adv_commision_amount) + "%" + " advance" response = { 'is_error': 0, 'message': 'Commission successfully added', 'comm_type': commission_type, 'comm_amt': commission } else: if response.get('is_error') == 1: response = { 'is_error': 1, 'message': response.get('message'), } return response def advance_pps_type_calc(self): adv_commision_amount, commision_value, commision_value_type = self.env["advance.commision"].calc_commision_adv(self.affiliate_program_id.advance_commision_id.id, self.type_id, self.unit_price) # argument of calc_commision_adv(adv_comm_id, product_id on which commision apply , price of product) # return adv_commision_amount ,commision_value, commision_value_type _logger.info("---11----adv_commision_amount----commision_value-%r--------commision_value_type-%r------", adv_commision_amount, commision_value, commision_value_type) # return commision_value,commision_value_type return adv_commision_amount, commision_value, commision_value_type @api.model def process_ppc_maturity_scheduler_queue(self): _logger.info("-----Inside----process_ppc_maturity_scheduler_queue-----------") check_enable_ppc = self.env['res.config.settings'].sudo().website_constant().get('enable_ppc') all_ppc_visits = self.search([('affiliate_method', '=', 'ppc'), ('state', '=', 'draft')]) if check_enable_ppc: for visit in all_ppc_visits: visit.action_confirm()