200 lines
8.6 KiB
Python
200 lines
8.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import models, fields, api, _
|
|
from odoo.fields import Domain
|
|
from odoo.exceptions import UserError
|
|
from itertools import groupby
|
|
from collections import Counter
|
|
|
|
|
|
class PosSession(models.Model):
|
|
_inherit = "pos.session"
|
|
|
|
cash_box_opening_number = fields.Integer(
|
|
help="Count the number of cashbox opening during the session"
|
|
)
|
|
users_clocked_ids = fields.Many2many(
|
|
"res.users",
|
|
"users_session_clocking_info",
|
|
string="Users Clocked In",
|
|
help="This is a technical field used for tracking the status of the session for each users.",
|
|
)
|
|
employees_clocked_ids = fields.Many2many(
|
|
"hr.employee",
|
|
"employees_session_clocking_info",
|
|
string="Employees Clocked In",
|
|
help="This is a technical field used for tracking the status of the session for each employees.",
|
|
)
|
|
|
|
pro_forma_sales_number = fields.Integer()
|
|
pro_forma_sales_amount = fields.Monetary()
|
|
pro_forma_refund_number = fields.Integer()
|
|
pro_forma_refund_amount = fields.Monetary()
|
|
|
|
correction_number = fields.Integer(
|
|
help="Count the number of corrections during the session"
|
|
)
|
|
correction_amount = fields.Monetary(
|
|
help="Sum of the amount of the corrections during the session"
|
|
)
|
|
|
|
def _load_pos_data_read(self, records, config):
|
|
read_records = super()._load_pos_data_read(records, config)
|
|
if read_records and config._uses_blackbox_v1():
|
|
record = read_records[0]
|
|
session = self if self.exists() else self.env["pos.session"].browse(record["id"]).exists()
|
|
record["_users_clocked_ids"] = session.users_clocked_ids.ids if session else []
|
|
record["_employees_clocked_ids"] = session.employees_clocked_ids.ids if session else []
|
|
record["_user_latest_clock_in"] = session._get_latest_user_clockin_date(config) if session else None
|
|
if config.module_pos_hr:
|
|
employees_insz_or_bis_number = self.env['hr.employee'].sudo().search_read(config._employee_domain(config.current_user_id.id), ['id', 'insz_or_bis_number'])
|
|
insz_or_bis_number_per_employee_id = {employee['id']: employee['insz_or_bis_number'] for employee in employees_insz_or_bis_number}
|
|
record["_employee_insz_or_bis_number"] = insz_or_bis_number_per_employee_id
|
|
return read_records
|
|
|
|
@api.depends("order_ids")
|
|
def _compute_amount_of_vat_tickets(self):
|
|
for rec in self:
|
|
rec.amount_of_vat_tickets = len(rec.order_ids)
|
|
|
|
def _set_opening_control_data(self, cashbox_value: int, notes: str):
|
|
self.env['pos.blackbox.log.ip']._log_ip(self.config_id, None)
|
|
super()._set_opening_control_data(cashbox_value, notes)
|
|
|
|
def increase_cash_box_opening_counter(self):
|
|
self.cash_box_opening_number += 1
|
|
|
|
def increase_correction_counter(self, amount):
|
|
self.correction_number += 1
|
|
self.correction_amount += self.currency_id.round(amount)
|
|
|
|
def set_user_session_work_status(self, user_id, status, all_insz):
|
|
context = (
|
|
"employees_clocked_ids"
|
|
if self.config_id.module_pos_hr
|
|
else "users_clocked_ids"
|
|
)
|
|
if all_insz:
|
|
self.write({context: [(5,)]})
|
|
elif status:
|
|
self.write({context: [(4, user_id)]})
|
|
else:
|
|
self.write({context: [(3, user_id)]})
|
|
self.config_id._notify("CLOCKING", {
|
|
'session_id': self.id,
|
|
'data': {
|
|
'pos.session': self._load_pos_data_read(self, self.config_id),
|
|
}
|
|
})
|
|
|
|
def _get_user_report_data(self):
|
|
data = []
|
|
|
|
orders = self.order_ids[::-1] # orders are sorted by date_order asc and we want date_order desc, then it needs to be filtered by user_id and employee_id
|
|
orders = orders.sorted(lambda order: order.employee_id.id or order.user_id.id) # if one order has an employee id, every order should have one. Otherwise, no orders has an employee id.
|
|
|
|
for k, g in groupby(orders, lambda order: order.employee_id or order.user_id):
|
|
insz = k.sudo().insz_or_bis_number
|
|
for order in g:
|
|
if order.is_clock:
|
|
if order.lines[0].product_id.id == self.env.ref('pos_blackbox_be.product_product_work_in').id:
|
|
data.append({
|
|
'login': k.name,
|
|
'insz_or_bis_number': insz,
|
|
'revenue': 0,
|
|
'revenue_per_category': Counter(),
|
|
'first_ticket_time': order.date_order,
|
|
'last_ticket_time': False,
|
|
'fdmIdentifier': self.config_id.certified_blackbox_identifier,
|
|
'cash_rounding_applied': 0,
|
|
})
|
|
else:
|
|
data[-1]['last_ticket_time'] = order.date_order
|
|
elif len(data) > 0 and not data[-1]['last_ticket_time']:
|
|
data[-1]['revenue'] += order.amount_paid
|
|
data[-1]['cash_rounding_applied'] += self.currency_id.round(order.amount_total - order.amount_paid)
|
|
total_sold_per_category = {}
|
|
for line in order.lines:
|
|
category_name = line.product_id.pos_categ_ids[0].name if len(line.product_id.pos_categ_ids) > 0 else "None"
|
|
if category_name not in total_sold_per_category:
|
|
total_sold_per_category[category_name] = 0
|
|
total_sold_per_category[category_name] += self.currency_id.round(line.price_subtotal_incl)
|
|
|
|
data[-1]['revenue_per_category'].update(Counter(total_sold_per_category))
|
|
|
|
for info in data:
|
|
info['revenue_per_category'] = list(info['revenue_per_category'].items())
|
|
|
|
return data
|
|
|
|
def action_report_journal_file(self):
|
|
self.ensure_one()
|
|
pos = self.config_id
|
|
if not pos.iface_fiscal_data_module:
|
|
raise UserError(_("PoS %s is not a certified PoS", pos.name))
|
|
return {
|
|
"type": "ir.actions.act_url",
|
|
"url": "/journal_file/" + str(pos.certified_blackbox_identifier),
|
|
"target": "self",
|
|
}
|
|
|
|
def _update_pro_forma(self, order):
|
|
self.ensure_one()
|
|
if order['state'] == "draft":
|
|
amount_total = order['amount_total']
|
|
if amount_total < 0:
|
|
self.pro_forma_refund_number += 1
|
|
self.pro_forma_refund_amount += self.currency_id.round(amount_total)
|
|
else:
|
|
self.pro_forma_sales_number += 1
|
|
self.pro_forma_sales_amount += self.currency_id.round(amount_total)
|
|
|
|
def get_total_discount_positive_negative(self, positive):
|
|
order_ids = self.order_ids.ids
|
|
price_operator = ">=" if positive else "<"
|
|
|
|
orderlines = self.env["pos.order.line"].search(
|
|
[("order_id", "in", order_ids), ("price_subtotal_incl", price_operator, 0), ("discount", ">", 0)]
|
|
)
|
|
|
|
amount = sum(
|
|
line._get_discount_amount()
|
|
for line in orderlines
|
|
)
|
|
|
|
return self.currency_id.round(amount)
|
|
|
|
def check_everyone_is_clocked_out(self):
|
|
if (
|
|
self.config_id.module_pos_hr and len(self.employees_clocked_ids.ids) > 0
|
|
) or (
|
|
not self.config_id.module_pos_hr and len(self.users_clocked_ids.ids) > 0
|
|
):
|
|
raise UserError(_("You cannot close the POS with employees still clocked in. Please clock them out first."))
|
|
|
|
def get_insz_clocked(self):
|
|
insz_map = {}
|
|
if self.config_id.module_pos_hr:
|
|
for employee in self.employees_clocked_ids:
|
|
insz_map[employee.id] = employee.sudo().insz_or_bis_number
|
|
else:
|
|
for user in self.users_clocked_ids:
|
|
insz_map[user.id] = user.sudo().insz_or_bis_number
|
|
return insz_map
|
|
|
|
def _get_latest_user_clockin_date(self, config):
|
|
self.ensure_one()
|
|
|
|
work_in_product = self.env.ref('pos_blackbox_be.product_product_work_in', raise_if_not_found=False)
|
|
if not work_in_product:
|
|
return None
|
|
|
|
domain = Domain('lines.product_id', '=', work_in_product.id)
|
|
if config.module_pos_hr:
|
|
domain &= Domain('employee_id', '=', self.employee_id.id)
|
|
else:
|
|
domain &= Domain('user_id', '=', self.user_id.id)
|
|
order = self.env['pos.order'].search(domain, order='id desc', limit=1)
|
|
return order.date_order if order else None
|