348 lines
18 KiB
Python
348 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
|
#################################################################################
|
|
# Author : Webkul Software Pvt. Ltd. (<https://webkul.com/>: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 <https://store.webkul.com/license.html/>;
|
|
#################################################################################
|
|
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()
|