# -*- coding: utf-8 -*- from datetime import datetime, date, timedelta from dateutil.relativedelta import relativedelta from odoo import fields, api, models, _ from odoo.exceptions import UserError, ValidationError class RentalProductContract(models.Model): _name = "rental.product.contract" _description = "Product Rental Contract" name = fields.Char("Name") partner_id = fields.Many2one("res.partner", string="Customer", required=True) rental_id = fields.Many2one("rental.product.order", string="Rental Order Id") contract_date = fields.Date( string="Contract Date", ) contractor_id = fields.Many2one("res.users", string="Contractor Name") from_date = fields.Date(string="From Date") to_date = fields.Date(string="To Date") account_payment_term = fields.Many2one( "account.payment.term", string="Payment Term", required=True ) damage_charge = fields.Monetary(string="Damage Charge") additional_charges = fields.Monetary(string="Additional Charges") subtotal = fields.Monetary(string="Sub Total", readonly=True) taxes = fields.Float(string="Taxes", compute="_compute_amount", readonly=True) untaxed_amount = fields.Monetary( string="Untaxed Amount", compute="_compute_amount", ) extra_charges = fields.Monetary(string="Extra Charges") invoice_ids = fields.Many2one("account.move", string="Invoice Id") signature = fields.Binary(string="Contractor Signature ") signature_contractor = fields.Binary(string="Contractor Signature") signature_customer = fields.Binary(string="Customer Signature") button_name = fields.Char(string="Button Name") terms_condition = fields.Text(string="Terms and Condition") product_contract_lines_ids = fields.One2many( "product.contract.lines", "product_contract_id", string="Order Line" ) pricelist_id = fields.Many2one("product.pricelist", string="Pricelist") total_amount = fields.Monetary(string="Total Amount", compute="_compute_amount") total = fields.Monetary(string="Total", compute="_compute_total") cost_generated = fields.Monetary( string="Recurring Cost", help="Costs paid at regular intervals, depending on the cost frequency", ) cost_frequency = fields.Selection( [ ("no", "No"), ("daily", "Daily"), ("weekly", "Weekly"), ("monthly", "Monthly"), ("yearly", "Yearly"), ], string="Recurring Cost Frequency", required=True, ) state = fields.Selection( [ ("futur", "Incoming"), ("open", "In Progress"), ("expired", "Expired"), ("diesoon", "Expiring Soon"), ("closed", "Closed"), ], "Status", default="open", readonly=True, ) cost = fields.Monetary( string="Rent Cost", help="This fields is to determine the cost of rent", required=True, ) account_type = fields.Many2one( "account.account", "Account", default=lambda self: self.env["account.account"].search([("id", "=", 17)]), ) recurring_line = fields.One2many( "product.rental.line", "rental_number", readonly=True ) attachment_ids = fields.Many2many( "ir.attachment", "product_rent_ir_attachments_rel", "rental_id", "attachment_id", string="Attachments", ) sum_cost = fields.Float( compute="_compute_sum_cost", string="Indicative Costs Total" ) auto_generated = fields.Boolean("Automatically Generated", readonly=True) generated_cost_ids = fields.One2many( "product.rental.line", "rental_number", string="Generated Costs" ) invoice_count = fields.Integer( compute="_invoice_count", string="# Invoice", copy=False ) first_payment = fields.Float(string="First Payment", compute="_compute_amount") first_invoice_created = fields.Boolean( string="First Invoice Created", default=False ) origin = fields.Char(string="Order Reference") picking_id = fields.Many2one("stock.picking", string="Picking") document_ids = fields.One2many( "customer.document", "contract_id", string="Contract" ) company_id = fields.Many2one( "res.company", string="Company", default=lambda self: self.env.user.company_id ) currency_id = fields.Many2one("res.currency", related="company_id.currency_id") cancel_policy_ids = fields.One2many( "rental.policy", "contract_id", string="Cancel Policy" ) number_of_slot = fields.Integer(string="Number of Slot") is_hours = fields.Boolean(string="Hours") is_days = fields.Boolean(string="Days") def generate_policy(self): if not self.cancel_policy_ids and self.number_of_slot != 0: number_of_days = self.from_date - self.contract_date cancel_policy_list = [] if number_of_days.days >= (self.number_of_slot * 2): day_per_slot = int(number_of_days.days / self.number_of_slot - 1) day = 0 for i in range(self.number_of_slot - 1): cancel_policy_list.append( ( 0, 0, { "from_date": self.contract_date + timedelta(day), "to_date": self.contract_date + timedelta(day_per_slot + day), }, ) ) day += day_per_slot + 1 cancel_policy_list.append( ( 0, 0, { "from_date": self.contract_date + timedelta(day), "to_date": self.from_date - timedelta(days=2), }, ) ) cancel_policy_list.append( ( 0, 0, { "from_date": self.from_date - timedelta(days=1), "policy_charged": 100, }, ) ) self.cancel_policy_ids = cancel_policy_list else: raise ValidationError(_("Please enter the sufficient Number of Slot")) def write(self, vals): if "button_name" in vals.keys(): if vals["button_name"] == "signature_contractor": vals["signature_contractor"] = vals["signature"] elif vals["button_name"] == "signature_customer": vals["signature_customer"] = vals["signature"] return super(RentalProductContract, self).write(vals) @api.depends("product_contract_lines_ids") def _compute_amount(self): """ Compute the total amounts of the SO. """ for order in self: untaxed_amount = 0.0 taxes = 0.0 total_amount = 0.0 for line in order.product_contract_lines_ids: untaxed_amount += line.sub_total taxes += line.price_tax total_amount += line.sub_total + line.price_tax order.update( { "untaxed_amount": untaxed_amount, "taxes": taxes, "total_amount": untaxed_amount + taxes + order.extra_charges, "first_payment": untaxed_amount + taxes + order.extra_charges, } ) @api.depends("recurring_line.recurring_amount") def _compute_sum_cost(self): for contract in self: contract.sum_cost = sum(contract.recurring_line.mapped("recurring_amount")) def _invoice_count(self): invoice_ids = self.env["account.move"].search( [("invoice_origin", "=", self.name)] ) self.invoice_count = len(invoice_ids) @api.model def create(self, vals): sequence_no = self.env["ir.sequence"].next_by_code("product_contract") or _( "Product Contract" ) vals.update({"name": sequence_no}) return super(RentalProductContract, self).create(vals) @api.depends("product_contract_lines_ids", "damage_charge") def _compute_total(self): self.total = self.total_amount + self.damage_charge def contract_close(self): invoice_ids = self.env["account.move"].search( [("invoice_origin", "=", self.name)] ) order_ids = self.env["rental.product.order"].search( [("res_number", "=", self.origin)] ) is_paid = 0 for each in invoice_ids: if each.state != "posted": is_paid = 1 break if is_paid == 0: self.state = "closed" order_ids.state = "close" else: raise UserError("Please Check invoices There are Some Invoices are pending") def contract_open(self): for record in self: order_ids = self.env["rental.product.order"].search( [("res_number", "=", record.origin)] ) record.state = "open" order_ids.state = "draft" def act_renew_contract(self): product_list = [] for product_line in self.product_contract_lines_ids: product_list.append( ( 0, 0, { "product_id": product_line.product_id.id, "price_based": product_line.price_based, "enter_days": product_line.enter_days, "price": product_line.price, "enter_hour": product_line.enter_hour, }, ) ) assert ( len(self.ids) == 1 ), "This operation should only be done for 1 single contract at a time, as it it suppose to open a window as result" for element in self: # compute end date startdate = fields.Date.from_string(element.from_date) enddate = fields.Date.from_string(element.to_date) diffdate = enddate - startdate default = { "contract_date": fields.Date.context_today(self), "from_date": fields.Date.to_string( fields.Date.from_string(element.to_date) + relativedelta(days=1) ), "to_date": fields.Date.to_string(enddate + diffdate), "cost_generated": self.cost_generated, "product_contract_lines_ids": product_list, } newid = element.copy(default).id return { "name": _("Renew Contract"), "view_mode": "form", "view_id": self.env.ref( "product_rental_bookings.rental_product_contract_form" ).id, "view_type": "tree,form", "res_model": "rental.product.contract", "type": "ir.actions.act_window", "res_id": newid, } def send_product_contract(self): """ This is Email for send contract Detail """ self.ensure_one() ir_model_data = self.env["ir.model.data"] try: template_id = ir_model_data.get_object_reference( "product_rental_bookings", "email_template_product_contract" )[1] except ValueError: template_id = False try: compose_form_id = ir_model_data.get_object_reference( "mail", "email_compose_message_wizard_form" )[1] except ValueError: compose_form_id = False ctx = { "default_model": "rental.product.contract", "default_res_id": self.ids[0], "default_use_template": bool(template_id), "default_template_id": template_id, "mark_so_as_sent": True, } return { "type": "ir.actions.act_window", "view_type": "form", "view_mode": "form", "res_model": "mail.compose.message", "views": [(compose_form_id, "form")], "view_id": compose_form_id, "target": "new", "context": ctx, } def send_email_for_firstpayment(self, inv_id, contracts): """ Send email for payment. """ mail_content = _( "

First Payment!


Hi %s,
This is to notify that You have to pay amount as per mention below.

" "Please find the details below:

" "" "
Reference Number %s
Date %s
Amount %s
" ) % ( contracts.partner_id.name, inv_id.invoice_origin, date.today(), inv_id.amount_total, ) main_content = { "subject": _("You First Payment For: %s") % inv_id.invoice_origin, "author_id": contracts.env.user.partner_id.id, "body_html": mail_content, "email_to": contracts.partner_id.email, } self.env["mail.mail"].create(main_content).send() def notification_email_for_expire_contract(self, contracts): mail_content = _( "

Expiration Of Rental Contract

" "
Dear %s,
" "Our record indicate that your rental contract %s, expire soon,
" "If you want to renew this contract Then contact to our agency before last date of contract." "
" "
" "
" "
" "
" "" "" "" "" "
Contract Ref" "%s" "
Responsible Person " " %s - %s" "
" ) % ( contracts.partner_id.name, contracts.name, contracts.name, contracts.contractor_id.name, contracts.contractor_id.mobile, ) main_content = { "subject": "Expiration Of Rental Contract!", "author_id": contracts.env.user.partner_id.id, "body_html": mail_content, "email_to": contracts.partner_id.email, } self.env["mail.mail"].create(main_content).send() def send_email_for_recurring_invoice(self, inv_id, contracts): mail_content = _( "

Reminder Recurrent Payment!

" "
Hi %s,
This is to remind you that the " "recurrent payment for the " "rental contract has to be done." "Please make the payment at the earliest." "
" "
" "Please find the details below:" "
" "
" "
" "" "" "" "" "" "" "
Amount " " %s" "
Due Date " " %s" "
Responsible Person " " %s, %s" "
" ) % ( contracts.partner_id.name, inv_id.amount_total, date.today(), inv_id.user_id.name, inv_id.user_id.mobile, ) main_content = { "subject": "Reminder Recurrent Payment!", "author_id": contracts.env.user.partner_id.id, "body_html": mail_content, "email_to": contracts.partner_id.email, } self.env["mail.mail"].create(main_content).send() def create_invoice(self): inv_obj = self.env["account.move"] recurring_obj = self.env["product.rental.line"] inv_line = [] today = date.today() journal_id = ( self.env["account.journal"] .sudo() .search( [("type", "=", "sale"), ("company_id", "=", self.company_id.id)], limit=1, ) ) for contracts in self.search([]): if not contracts.first_invoice_created: contracts.first_invoice_created = True supplier = contracts.partner_id account_id = self.env["account.account"].search( [ ("code", "like", "708000"), ("company_id", "=", self.company_id.id), ] ) if not account_id: user_type_id = self.env.ref("account.data_account_type_revenue") account_id = self.env["account.account"].create( { "code": "708000", "name": "Location", "company_id": self.company_id.id, "user_type_id": user_type_id.id, } ) for each_product in contracts.product_contract_lines_ids: if each_product.price_based == "per_hour": total_qty = each_product.enter_hour else: total_qty = each_product.enter_days inv_line_data = ( 0, 0, { "name": each_product.product_id.name or "Deposit", "product_id": each_product.product_id.id or False, "product_id": each_product.product_id.id or False, "account_id": account_id.id, "price_unit": each_product.price * total_qty or 0.0, "quantity": each_product.qty_needed, "enter_hour": each_product.enter_hour, "enter_days": each_product.enter_days, "tax_ids": [(6, 0, each_product.tax_id.ids)], }, ) inv_line.append(inv_line_data) if self.extra_charges: extra_charge_p_id = self.env.ref( "product_rental_bookings.extra_charge_product_id" ) extra_charge_inv_line = ( 0, 0, { "name": extra_charge_p_id.name, "product_id": extra_charge_p_id.id or False, "price_unit": self.extra_charges, "account_id": account_id.id, "quantity": 1.0, }, ) inv_line.append(extra_charge_inv_line) inv_data = { "move_type": "out_invoice", "amount_residual": self.total_amount, "currency_id": self.env.company.currency_id.id, "journal_id": journal_id.id, "company_id": self.env.company.id, "partner_id": supplier.id, "invoice_date_due": self.to_date, "invoice_origin": contracts.name, "contract_id": self.id, "is_hours": self.is_hours, "is_days": self.is_days, "rental_order_id": self.rental_id.id, "invoice_line_ids": inv_line, } bokeh = self.env["ir.module.module"].search( [("name", "in", ["l10n_in", "l10n_in_purchase", "l10n_in_sale"])], limit=1, ) if bokeh and bokeh.state == "installed": inv_data.update( { "l10n_in_gst_treatment": supplier.l10n_in_gst_treatment or "unregistered", } ) inv_id = inv_obj.create(inv_data) inv_id.action_post() recurring_data = { "name": "demo", "date_today": today, "rental_number": contracts.id, "recurring_amount": contracts.first_payment, "invoice_number": inv_id.id, "invoice_ref": inv_id.id, } recurring_obj.create(recurring_data) # self.send_email_for_firstpayment(inv_id, contracts) if inv_id: return { "name": _("Account Move"), "view_mode": "form", "view_id": self.env.ref("account.view_move_form").id, "view_type": "tree,form", "res_model": "account.move", "type": "ir.actions.act_window", "res_id": inv_id.id, } @api.model def scheduler_manage_invoice(self): journal_id = self.env["account.move"].default_get(["journal_id"])["journal_id"] inv_obj = self.env["account.move"] recurring_obj = self.env["product.rental.line"] _inv_line_data = {} today = date.today() for contracts in self.search([]): account_id = self.env["account.account"].search( [ ("code", "like", "708000"), ("company_id", "=", contracts.company_id.id), ] ) if not account_id: user_type_id = self.env.ref("account.data_account_type_revenue") account_id = self.env["account.account"].create( { "code": "708000", "name": "Location", "company_id": contracts.company_id.id, "user_type_id": user_type_id.id, } ) start_date = datetime.strptime(str(contracts.from_date), "%Y-%m-%d").date() end_date = datetime.strptime(str(contracts.to_date), "%Y-%m-%d").date() if end_date >= date.today(): is_recurring = 0 if contracts.cost_frequency == "daily": is_recurring = 1 elif contracts.cost_frequency == "weekly": week_days = (date.today() - start_date).days if week_days % 7 == 0 and week_days != 0: is_recurring = 1 elif contracts.cost_frequency == "monthly": if ( start_date.day == date.today().day and start_date != date.today() ): is_recurring = 1 elif contracts.cost_frequency == "yearly": if ( start_date.day == date.today().day and start_date.month == date.today().month and start_date != date.today() ): is_recurring = 1 if ( is_recurring == 1 and contracts.cost_frequency != "no" and contracts.state != "expire" and contracts.state != "close" and contracts.state != "futur" and contracts.first_invoice_created == True ): inv_line = [] supplier = contracts.partner_id line_len = len(contracts.product_contract_lines_ids) for each_product in contracts.product_contract_lines_ids: unit_price = contracts.cost_generated / line_len inv_line_data = ( 0, 0, { "product_id": each_product.product_id.id, "name": each_product.product_id.name, "product_id": each_product.product_id.id, "account_id": account_id.id, "price_unit": float(unit_price), "quantity": 1, "exclude_from_invoice_tab": False, }, ) inv_line.append(inv_line_data) inv_data = { "type": "out_invoice", "currency_id": contracts.account_type.company_id.currency_id.id, "journal_id": journal_id, "company_id": contracts.account_type.company_id.id, "name": supplier.name, "partner_id": supplier.id, "invoice_date_due": contracts.to_date, "invoice_origin": contracts.name, "contract_id": contracts.id, "invoice_line_ids": inv_line, "is_hours": contracts.is_hours, "is_days": contracts.is_days, } inv_id = inv_obj.create(inv_data) payment_id = self.env["account.payment"].create( { "payment_type": "inbound", "partner_type": "supplier", "partner_id": supplier.id, "amount": inv_id.amount_total, "journal_id": journal_id, "payment_date": date.today(), "payment_method_id": "1", "communication": inv_id.name, } ) recurring_data = { "name": "demo", "date_today": today, "rental_number": contracts.id, "recurring_amount": contracts.cost_generated, "invoice_number": inv_id.id, "invoice_ref": inv_id.id, } recurring_obj.create(recurring_data) # self.send_email_for_recurring_invoice(inv_id, contracts) else: inv_line = [] if ( not contracts.first_invoice_created and contracts.state != "futur" and contracts.state != "expired" ): contracts.first_invoice_created = True supplier = contracts.partner_id for each_product in contracts.product_contract_lines_ids: if each_product.price_based == "per_day": total_qty = each_product.enter_days else: total_qty = each_product.enter_hour inv_line_data = ( 0, 0, { "product_id": each_product.product_id.id, "name": each_product.product_id.name, "product_id": each_product.product_id.id, "account_id": supplier.property_account_payable_id.id, "price_unit": each_product.price, "quantity": total_qty, "tax_ids": [(6, 0, each_product.tax_id.ids)], "exclude_from_invoice_tab": False, }, ) inv_line.append(inv_line_data) inv_data = { "name": supplier.name, "partner_id": supplier.id, "currency_id": contracts.account_type.company_id.currency_id.id, "journal_id": journal_id, "invoice_origin": contracts.name, "company_id": contracts.account_type.company_id.id, "invoice_date_due": self.to_date, "invoice_line_ids": inv_line, } inv_id = inv_obj.create(inv_data) recurring_data = { "name": "demo", "date_today": today, "rental_number": contracts.id, "recurring_amount": contracts.first_payment, "invoice_number": inv_id.id, "invoice_ref": inv_id.id, } recurring_obj.create(recurring_data) # self.send_email_for_firstpayment(inv_id, contracts) @api.model def shedule_manage_contract(self): date_today = fields.Date.from_string(fields.Date.today()) in_fifteen_days = fields.Date.to_string(date_today + relativedelta(days=+15)) nearly_expired_contracts = self.search( [("state", "=", "open"), ("to_date", "<", in_fifteen_days)] ) res = {} for contract in nearly_expired_contracts: if contract.partner_id.id in res: res[contract.partner_id.id] += 1 else: res[contract.partner_id.id] = 1 # contract.notification_email_for_expire_contract(contract) nearly_expired_contracts.write({"state": "diesoon"}) expired_contracts = self.search( [("state", "!=", "expired"), ("to_date", "<", fields.Date.today())] ) expired_contracts.write({"state": "expired"}) futur_contracts = self.search( [ ("state", "not in", ["futur", "closed"]), ("from_date", ">", fields.Date.today()), ] ) futur_contracts.write({"state": "futur"}) now_running_contracts = self.search( [("state", "=", "futur"), ("from_date", "<=", fields.Date.today())] ) now_running_contracts.write({"state": "open"}) @api.model def run_scheduler(self): self.shedule_manage_contract() self.scheduler_manage_invoice() class productConLines(models.Model): _name = "product.contract.lines" _description = "Product Rental Contract Lines" product_id = fields.Many2one("product.product", string="product Name") price_based = fields.Selection( [("per_day", "Day"), ("per_hour", "Hour"), ("per_session", "Session")], default="per_day", string="Based On", ) enter_hour = fields.Float(string="Hour") enter_days = fields.Float(string="Days") price = fields.Monetary(string="Price") total = fields.Monetary(string="Total") product_contract_id = fields.Many2one("rental.product.contract", string="Contract") tax_id = fields.Many2many("account.tax", "product_contract_tax_rel", string="Tax") sub_total = fields.Monetary(string="Sub Total", compute="_get_subtotal", store=True) price_tax = fields.Float( compute="_get_subtotal", string="Taxes", readonly=True, store=True ) price_total = fields.Monetary( compute="_get_subtotal", string="Total ", readonly=True, store=True ) description = fields.Char(string="Description") currency_id = fields.Many2one( "res.currency", related="product_contract_id.currency_id", store=True, readonly=True, ) qty_needed = fields.Integer(string="Quantity", default=1) @api.depends("product_id", "enter_hour", "enter_days", "price", "tax_id") def _get_subtotal(self): for line in self: if line.price_based == "per_day": qty = line.enter_days * line.qty_needed elif line.price_based == "per_session": qty = line.qty_needed else: qty = line.enter_hour * line.qty_needed print("\n\n\n\n\n\n------>", qty) taxes = line.tax_id.compute_all( qty, line.product_contract_id.currency_id, line.price ) line.update( { "price_tax": sum( t.get("amount", 0.0) for t in taxes.get("taxes", []) ), "price_total": taxes["total_included"], "sub_total": taxes["total_excluded"], } ) class ProductRentalLine(models.Model): _name = "product.rental.line" _description = "Rental Lines" name = fields.Char(string="Name") date_today = fields.Date("Date") recurring_amount = fields.Float("Amount") rental_number = fields.Many2one("rental.product.contract", string="Rental Number") payment_info = fields.Char( compute="paid_info", string="Payment Stage", default="draft" ) auto_generated = fields.Boolean("Automatically Generated", readonly=True) invoice_number = fields.Integer(string="Invoice ID") invoice_ref = fields.Many2one("account.move", string="Invoice Ref") @api.depends("payment_info") def paid_info(self): for record in self: if self.env["account.move"].browse(record.invoice_number): record.payment_info = ( self.env["account.move"].browse(record.invoice_number).state ) else: record.payment_info = "Record Deleted" class CustomerDocument(models.Model): _name = "customer.document" _description = "Customer Document" name = fields.Binary(string="Document") id_number = fields.Char(string="ID Number") contract_id = fields.Many2one("rental.product.contract", string="Conrtract") class RentalPolicy(models.Model): _name = "rental.policy" _description = "Rental Policy" contract_id = fields.Many2one("rental.product.contract", string="Contract") from_date = fields.Date(string="From Date") to_date = fields.Date(string="To Date") policy_charged = fields.Float(string="Charge(In Percentage)")