mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 09:52:02 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -4,4 +4,6 @@
|
|||
from . import pos_details
|
||||
from . import pos_payment
|
||||
from . import pos_close_session_wizard
|
||||
from . import pos_session_check_product_wizard
|
||||
from . import pos_daily_sales_reports
|
||||
from . import pos_confirmation_wizard
|
||||
from . import pos_make_invoice
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from odoo import fields, models
|
|||
|
||||
|
||||
class PosCloseSessionWizard(models.TransientModel):
|
||||
_name = "pos.close.session.wizard"
|
||||
_name = 'pos.close.session.wizard'
|
||||
_description = "Close Session Wizard"
|
||||
|
||||
amount_to_balance = fields.Float("Amount to balance")
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@
|
|||
<group>
|
||||
<field name="account_readonly" invisible="1" />
|
||||
<field name="amount_to_balance" readonly="1" />
|
||||
<field name="account_id" attrs="{'readonly': [('account_readonly', '==', True)]}"/>
|
||||
<field name="account_id" readonly="account_readonly"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="close_session" string="Close Session" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button special="cancel" data-hotkey="z" string="Cancel" class="btn-secondary" />
|
||||
<button special="cancel" data-hotkey="x" string="Cancel" class="btn-secondary" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
from odoo import models, fields, api, _
|
||||
|
||||
class PosConfirmationWizard(models.TransientModel):
|
||||
_name = 'pos.confirmation.wizard'
|
||||
_description = 'Confirmation Wizard'
|
||||
|
||||
def get_selected_orders(self):
|
||||
selected_orders = self.env.context.get('orders')
|
||||
return self.env['pos.order'].browse(selected_orders)
|
||||
|
||||
def _default_message(self):
|
||||
selected_orders = self.get_selected_orders()
|
||||
customer_name = selected_orders.partner_id.name
|
||||
message = _("It seems that the POS order(s) %(order_ref)s do not have a customer.\n\nWould you like to set %(customer_name)s as the customer for the selected POS order(s)?", order_ref= ', '.join(selected_orders.filtered(lambda o: not o.partner_id).mapped('name')), customer_name=customer_name)
|
||||
return message
|
||||
|
||||
message = fields.Text(default=_default_message, readonly=True)
|
||||
|
||||
def action_confirm(self):
|
||||
selected_orders = self.get_selected_orders()
|
||||
selected_orders.write({'partner_id': selected_orders.partner_id.id})
|
||||
return {
|
||||
'name': _('Create Invoice(s)'),
|
||||
'view_mode': 'form',
|
||||
'view_id': self.env.ref('point_of_sale.view_pos_make_invoice').id,
|
||||
'res_model': 'pos.make.invoice',
|
||||
'target': 'new',
|
||||
'type': 'ir.actions.act_window',
|
||||
'context': {'active_ids': selected_orders.ids},
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="point_of_sale.view_confirm_action_wizard" model="ir.ui.view">
|
||||
<field name="name">pos.confirmation.wizard.form</field>
|
||||
<field name="model">pos.confirmation.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Warning" width="500px">
|
||||
<group>
|
||||
<field name="message" nolabel="1" readonly="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_confirm" string="Confirm" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_confirm_action_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Confirm Action</field>
|
||||
<field name="res_model">pos.confirmation.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="point_of_sale.view_confirm_action_wizard"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class PosDailySalesReportsWizard(models.TransientModel):
|
||||
_name = 'pos.daily.sales.reports.wizard'
|
||||
_description = 'Point of Sale Daily Report'
|
||||
|
||||
pos_session_id = fields.Many2one('pos.session', required=True)
|
||||
|
||||
def _get_report_data(self):
|
||||
return {'date_start': False, 'date_stop': False, 'config_ids': self.pos_session_id.config_id.ids, 'session_ids': self.pos_session_id.ids}
|
||||
|
||||
def generate_report(self):
|
||||
return self.env.ref('point_of_sale.sale_details_report').report_action([], data=self._get_report_data())
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_pos_daily_sales_reports_wizard" model="ir.ui.view">
|
||||
<field name="name">pos.daily.sales.reports.wizard.form</field>
|
||||
<field name="model">pos.daily.sales.reports.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Sales Details">
|
||||
<group name="pos_session_group">
|
||||
<field name="pos_session_id" mode="list"/>
|
||||
</group>
|
||||
|
||||
<footer>
|
||||
<button name="generate_report" string="Print" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_report_pos_daily_sales_reports" model="ir.actions.act_window">
|
||||
<field name="name">Session Report</field>
|
||||
<field name="res_model">pos.daily.sales.reports.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,31 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class PosDetails(models.TransientModel):
|
||||
class PosDetailsWizard(models.TransientModel):
|
||||
_name = 'pos.details.wizard'
|
||||
_description = 'Point of Sale Details Report'
|
||||
|
||||
def _default_start_date(self):
|
||||
""" Find the earliest start_date of the latests sessions """
|
||||
# restrict to configs available to the user
|
||||
config_ids = self.env['pos.config'].search([]).ids
|
||||
# exclude configs has not been opened for 2 days
|
||||
self.env.cr.execute("""
|
||||
SELECT
|
||||
max(start_at) as start,
|
||||
config_id
|
||||
FROM pos_session
|
||||
WHERE config_id = ANY(%s)
|
||||
AND start_at > (NOW() - INTERVAL '2 DAYS')
|
||||
GROUP BY config_id
|
||||
""", (config_ids,))
|
||||
latest_start_dates = [res['start'] for res in self.env.cr.dictfetchall()]
|
||||
# earliest of the latest sessions
|
||||
return latest_start_dates and min(latest_start_dates) or fields.Datetime.now()
|
||||
values = self.env['pos.session']._read_group([
|
||||
('config_id', '!=', False),
|
||||
('start_at', '>', self.env.cr.now() - timedelta(days=2))
|
||||
], groupby=['config_id'], aggregates=['start_at:max'])
|
||||
mapping = dict(values)
|
||||
return (mapping and min(mapping.values())) or self.env.cr.now()
|
||||
|
||||
start_date = fields.Datetime(required=True, default=_default_start_date)
|
||||
end_date = fields.Datetime(required=True, default=fields.Datetime.now)
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@
|
|||
<field name="end_date"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="pos_config_ids" mode="tree" colspan="2" nolabel="1" />
|
||||
<field name="pos_config_ids" mode="list" nolabel="1" />
|
||||
</group>
|
||||
<footer>
|
||||
<button name="generate_report" string="Print" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z" />
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
from odoo import models, fields, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class PosMakeInvoice(models.TransientModel):
|
||||
_name = 'pos.make.invoice'
|
||||
_description = 'Multiple order invoice creation'
|
||||
|
||||
consolidated_billing = fields.Boolean(
|
||||
string="Consolidated Billing", default=True,
|
||||
help="Create one invoice for all orders related to same customer and same invoicing address"
|
||||
)
|
||||
count = fields.Integer(string="Order Count", compute='_compute_order_count')
|
||||
|
||||
def _compute_order_count(self):
|
||||
for wizard in self:
|
||||
wizard.count = len(self.env.context.get('active_ids', []))
|
||||
|
||||
def action_create_invoices(self):
|
||||
self.ensure_one()
|
||||
selected_orders = self.env['pos.order'].browse(self.env.context.get('active_ids')).filtered(lambda o: o.invoice_status == 'to_invoice' and o.state != 'draft' and o.state != 'cancel')
|
||||
if not selected_orders:
|
||||
raise UserError(_("No valid orders were selected. No new invoices could be generated"))
|
||||
|
||||
is_single_order = len(selected_orders) == 1
|
||||
|
||||
invalid_refund_orders = selected_orders.filtered(lambda o: o.refunded_order_id.account_move)
|
||||
if (not is_single_order) and invalid_refund_orders:
|
||||
# Normally it can't be encountered because when paying a refund order,
|
||||
# if the original order is invoiced, the refund order is required by the UI to be invoiced.
|
||||
order_names = "\n".join([f"{o.name} ({o.pos_reference})" for o in invalid_refund_orders])
|
||||
raise UserError(_("The following refund orders can't be part of a consolidated invoice because they refunded invoiced orders. Each refund order should be handled separately.\n\n%s", order_names))
|
||||
|
||||
invoices = self.env['account.move']
|
||||
|
||||
if not self.consolidated_billing or len(selected_orders) == 1:
|
||||
for order in selected_orders:
|
||||
invoices |= order._generate_pos_order_invoice()
|
||||
else:
|
||||
configs = selected_orders.config_id
|
||||
partners = selected_orders.partner_id
|
||||
some_order_has_no_partner = any(not o.partner_id for o in selected_orders)
|
||||
if len(configs) == 1 and len(partners) == 1 and some_order_has_no_partner:
|
||||
# When all the orders belong to one config and there is only one customer but some orders have no partner,
|
||||
# we can proceed but we ask the user if we proceed by setting that one customer to all the orders.
|
||||
return {
|
||||
'name': _('Warning'),
|
||||
'view_mode': 'form',
|
||||
'view_id': self.env.ref('point_of_sale.view_confirm_action_wizard').id,
|
||||
'res_model': 'pos.confirmation.wizard',
|
||||
'target': 'new',
|
||||
'type': 'ir.actions.act_window',
|
||||
'context': {'orders': selected_orders.ids, 'dialog_size': 'medium'},
|
||||
}
|
||||
|
||||
grouped_orders = []
|
||||
for config, config_orders in selected_orders.grouped('config_id').items():
|
||||
for partner, partner_orders in config_orders.grouped('partner_id').items():
|
||||
if not partner:
|
||||
raise UserError(_("Kindly ensure that each order contains a customer."))
|
||||
|
||||
for user, user_orders in partner_orders.grouped('user_id').items():
|
||||
for fiscal_position, fiscal_position_orders in user_orders.grouped('fiscal_position_id').items():
|
||||
grouped_orders.append(((config, partner, user, fiscal_position), fiscal_position_orders))
|
||||
|
||||
for _key, orders in grouped_orders:
|
||||
invoices |= orders._generate_pos_order_invoice()
|
||||
|
||||
if invoices:
|
||||
return selected_orders.action_view_invoice()
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="point_of_sale.view_pos_make_invoice" model="ir.ui.view">
|
||||
<field name="name">Create Invoice(s)</field>
|
||||
<field name="model">pos.make.invoice</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group>
|
||||
<field name="count"/>
|
||||
<field name="consolidated_billing" invisible="count == 1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_create_invoices" type="object"
|
||||
string="Create"
|
||||
class="btn-primary" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -22,8 +22,8 @@ class PosMakePayment(models.TransientModel):
|
|||
order = self.env['pos.order'].browse(active_id)
|
||||
amount_total = order.amount_total
|
||||
# If we refund the entire order, we refund what was paid originally, else we refund the value of the items returned
|
||||
if float_is_zero(order.refunded_order_ids.amount_total + order.amount_total, precision_rounding=order.currency_id.rounding):
|
||||
amount_total = -order.refunded_order_ids.amount_paid
|
||||
if float_is_zero(order.refunded_order_id.amount_total + order.amount_total, precision_rounding=order.currency_id.rounding):
|
||||
amount_total = -order.refunded_order_id.amount_paid
|
||||
return amount_total - order.amount_paid
|
||||
return False
|
||||
|
||||
|
|
@ -57,18 +57,20 @@ class PosMakePayment(models.TransientModel):
|
|||
currency = order.currency_id
|
||||
|
||||
init_data = self.read()[0]
|
||||
payment_method = self.env['pos.payment.method'].browse(init_data['payment_method_id'][0])
|
||||
if not float_is_zero(init_data['amount'], precision_rounding=currency.rounding):
|
||||
order.add_payment({
|
||||
'pos_order_id': order.id,
|
||||
'amount': order._get_rounded_amount(init_data['amount']),
|
||||
'amount': order._get_rounded_amount(init_data['amount'], payment_method.is_cash_count or not self.config_id.only_round_cash_method),
|
||||
'name': init_data['payment_name'],
|
||||
'payment_method_id': init_data['payment_method_id'][0],
|
||||
})
|
||||
|
||||
if order._is_pos_order_paid():
|
||||
order.action_pos_order_paid()
|
||||
order._create_order_picking()
|
||||
order._compute_total_cost_in_real_time()
|
||||
if order.state == 'draft' and order._is_pos_order_paid():
|
||||
order._process_saved_order(False)
|
||||
if order.state in {'paid', 'done'}:
|
||||
order._send_order()
|
||||
order.config_id.notify_synchronisation(order.config_id.current_session_id.id, 0)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
return self.launch_payment()
|
||||
|
|
|
|||
|
|
@ -13,14 +13,13 @@
|
|||
</group>
|
||||
<footer>
|
||||
<button name="check" string="Make Payment" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button special="cancel" data-hotkey="z" string="Cancel" class="btn-secondary"/>
|
||||
<button special="cancel" data-hotkey="x" string="Cancel" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_pos_payment" model="ir.actions.act_window">
|
||||
<field name="name">Payment</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.make.payment</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
from odoo.exceptions import AccessDenied
|
||||
from odoo.tools import convert
|
||||
|
||||
|
||||
class PosSessionCheckProductWizard(models.TransientModel):
|
||||
_name = 'pos.session.check_product_wizard'
|
||||
_description = 'Verify if there are any products for the PoS'
|
||||
|
||||
def load_demo_products(self):
|
||||
if not self.env.user.has_group("point_of_sale.group_pos_user"):
|
||||
raise AccessDenied()
|
||||
convert.convert_file(self.env.cr, 'point_of_sale', 'data/point_of_sale_onboarding.xml', None, mode='init', kind='data')
|
||||
return self.open_ui()
|
||||
|
||||
def open_ui(self):
|
||||
config = self.env['pos.config'].browse(self.env.context.get('config_id'))
|
||||
return config._action_to_open_ui()
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_pos_session_check_product_wizard" model="ir.ui.view">
|
||||
<field name="name">pos.session.check.product.wizard.form</field>
|
||||
<field name="model">pos.session.check_product_wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Check products">
|
||||
<p>
|
||||
You can add some from the Products menu, or linked any existing by flagging them as "Available in PoS".
|
||||
Or you can add demo data for testing purpose. Please mind that this is an irreversible action.
|
||||
</p>
|
||||
<footer>
|
||||
<button name="load_demo_products" string="Add demo data" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button name="open_ui" string="Continue without Demo data" type="object" class="btn-secondary" data-hotkey="z" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Add table
Add a link
Reference in a new issue