[REF] point_of_sale: split files per model
This commit is contained in:
committed by
Martin Trigaux
parent
d1c3c6518d
commit
fcdd43010a
@@ -30,28 +30,35 @@ Main Features
|
||||
""",
|
||||
'depends': ['sale_stock', 'barcodes'],
|
||||
'data': [
|
||||
'data/default_barcode_patterns.xml',
|
||||
'security/point_of_sale_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/default_barcode_patterns.xml',
|
||||
'wizard/pos_box.xml',
|
||||
'wizard/pos_confirm.xml',
|
||||
'wizard/pos_details.xml',
|
||||
'wizard/pos_discount.xml',
|
||||
'wizard/pos_open_statement.xml',
|
||||
'wizard/pos_payment.xml',
|
||||
'views/templates.xml',
|
||||
'point_of_sale.xml',
|
||||
'point_of_sale_report.xml',
|
||||
'point_of_sale_view.xml',
|
||||
'point_of_sale_sequence.xml',
|
||||
'point_of_sale_data.xml',
|
||||
'report/pos_order_report_view.xml',
|
||||
'point_of_sale_workflow.xml',
|
||||
'account_statement_view.xml',
|
||||
'account_statement_report.xml',
|
||||
'res_users_view.xml',
|
||||
'res_partner_view.xml',
|
||||
'res_config_view.xml',
|
||||
'views/pos_templates.xml',
|
||||
'views/point_of_sale_template.xml',
|
||||
'views/point_of_sale_report.xml',
|
||||
'views/point_of_sale_view.xml',
|
||||
'views/pos_order_view.xml',
|
||||
'views/product_view.xml',
|
||||
'views/pos_category_view.xml',
|
||||
'views/account_journal_view.xml',
|
||||
'views/pos_config_view.xml',
|
||||
'views/pos_session_view.xml',
|
||||
'views/account_invoice_view.xml',
|
||||
'views/point_of_sale_sequence.xml',
|
||||
'data/point_of_sale_data.xml',
|
||||
'data/point_of_sale_workflow.xml',
|
||||
'views/pos_order_report_view.xml',
|
||||
'views/account_statement_view.xml',
|
||||
'views/account_statement_report.xml',
|
||||
'views/res_users_view.xml',
|
||||
'views/res_partner_view.xml',
|
||||
'views/res_config_view.xml',
|
||||
'views/report_statement.xml',
|
||||
'views/report_usersproduct.xml',
|
||||
'views/report_saleslines.xml',
|
||||
@@ -60,10 +67,10 @@ Main Features
|
||||
'views/report_sessionsummary.xml',
|
||||
'views/report_userlabel.xml',
|
||||
'views/point_of_sale.xml',
|
||||
'point_of_sale_dashboard.xml',
|
||||
'views/point_of_sale_dashboard.xml',
|
||||
],
|
||||
'demo': [
|
||||
'point_of_sale_demo.xml',
|
||||
'data/point_of_sale_demo.xml',
|
||||
],
|
||||
'test': [
|
||||
'../account/test/account_minimal_test.xml',
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import account_bank_statement
|
||||
import point_of_sale
|
||||
import account_journal
|
||||
import barcode_rule
|
||||
import pos_category
|
||||
import pos_config
|
||||
import pos_order
|
||||
import pos_session
|
||||
import product
|
||||
import res_config
|
||||
import res_partner
|
||||
import res_users
|
||||
|
||||
@@ -5,14 +5,6 @@
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
class account_journal(osv.osv):
|
||||
_inherit = 'account.journal'
|
||||
_columns = {
|
||||
'journal_user': fields.boolean('Active in Point of Sale', help="Check this box if this journal define a payment method that can be used in a point of sale."),
|
||||
|
||||
'amount_authorized_diff' : fields.float('Amount Authorized Difference', help="This field depicts the maximum difference allowed between the ending balance and the theoretical cash when closing a session, for non-POS managers. If this maximum is reached, the user will have an error message at the closing of his session saying that he needs to contact his manager."),
|
||||
}
|
||||
|
||||
|
||||
class account_bank_statement(osv.osv):
|
||||
_inherit = 'account.bank.statement'
|
||||
@@ -20,3 +12,10 @@ class account_bank_statement(osv.osv):
|
||||
'pos_session_id' : fields.many2one('pos.session', string="Session", copy=False),
|
||||
'account_id': fields.related('journal_id', 'default_debit_account_id', type='many2one', relation='account.account', readonly=True),
|
||||
}
|
||||
|
||||
|
||||
class account_bank_statement_line(osv.osv):
|
||||
_inherit = 'account.bank.statement.line'
|
||||
_columns= {
|
||||
'pos_statement_id': fields.many2one('pos.order', string="POS statement", ondelete='cascade'),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
# Copyright (C) 2004-2008 PC Solutions (<http://pcsol.be>). All Rights Reserved
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
class account_journal(osv.osv):
|
||||
_inherit = 'account.journal'
|
||||
_columns = {
|
||||
'journal_user': fields.boolean('Active in Point of Sale', help="Check this box if this journal define a payment method that can be used in a point of sale."),
|
||||
|
||||
'amount_authorized_diff' : fields.float('Amount Authorized Difference', help="This field depicts the maximum difference allowed between the ending balance and the theoretical cash when closing a session, for non-POS managers. If this maximum is reached, the user will have an error message at the closing of his session saying that he needs to contact his manager."),
|
||||
}
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
|
||||
if not context:
|
||||
context = {}
|
||||
session_id = context.get('pos_session_id', False) or False
|
||||
|
||||
if session_id:
|
||||
session = self.pool.get('pos.session').browse(cr, uid, session_id, context=context)
|
||||
|
||||
if session:
|
||||
journal_ids = [journal.id for journal in session.config_id.journal_ids]
|
||||
args += [('id', 'in', journal_ids)]
|
||||
|
||||
return super(account_journal, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import sets
|
||||
from openerp import models
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class barcode_rule(models.Model):
|
||||
_inherit = 'barcode.rule'
|
||||
|
||||
def _get_type_selection(self):
|
||||
types = sets.Set(super(barcode_rule,self)._get_type_selection())
|
||||
types.update([
|
||||
('weight', _('Weighted Product')),
|
||||
('price', _('Priced Product')),
|
||||
('discount', _('Discounted Product')),
|
||||
('client', _('Client')),
|
||||
('cashier', _('Cashier'))
|
||||
])
|
||||
return list(types)
|
||||
@@ -0,0 +1,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
|
||||
class pos_category(osv.osv):
|
||||
_name = "pos.category"
|
||||
_description = "Public Category"
|
||||
_order = "sequence, name"
|
||||
|
||||
_constraints = [
|
||||
(osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
|
||||
]
|
||||
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
res = []
|
||||
for cat in self.browse(cr, uid, ids, context=context):
|
||||
names = [cat.name]
|
||||
pcat = cat.parent_id
|
||||
while pcat:
|
||||
names.append(pcat.name)
|
||||
pcat = pcat.parent_id
|
||||
res.append((cat.id, ' / '.join(reversed(names))))
|
||||
return res
|
||||
|
||||
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
|
||||
res = self.name_get(cr, uid, ids, context=context)
|
||||
return dict(res)
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True, translate=True),
|
||||
'complete_name': fields.function(_name_get_fnc, type="char", string='Name'),
|
||||
'parent_id': fields.many2one('pos.category','Parent Category', select=True),
|
||||
'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
|
||||
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
|
||||
|
||||
}
|
||||
|
||||
# NOTE: there is no 'default image', because by default we don't show
|
||||
# thumbnails for categories. However if we have a thumbnail for at least one
|
||||
# category, then we display a default image on the other, so that the
|
||||
# buttons have consistent styling.
|
||||
# In this case, the default image is set by the js code.
|
||||
image = openerp.fields.Binary("Image", attachment=True,
|
||||
help="This field holds the image used as image for the cateogry, limited to 1024x1024px.")
|
||||
image_medium = openerp.fields.Binary("Medium-sized image",
|
||||
compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
|
||||
help="Medium-sized image of the category. It is automatically "\
|
||||
"resized as a 128x128px image, with aspect ratio preserved. "\
|
||||
"Use this field in form views or some kanban views.")
|
||||
image_small = openerp.fields.Binary("Small-sized image",
|
||||
compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
|
||||
help="Small-sized image of the category. It is automatically "\
|
||||
"resized as a 64x64px image, with aspect ratio preserved. "\
|
||||
"Use this field anywhere a small image is required.")
|
||||
|
||||
@openerp.api.depends('image')
|
||||
def _compute_images(self):
|
||||
for rec in self:
|
||||
rec.image_medium = tools.image_resize_image_medium(rec.image)
|
||||
rec.image_small = tools.image_resize_image_small(rec.image)
|
||||
|
||||
def _inverse_image_medium(self):
|
||||
for rec in self:
|
||||
rec.image = tools.image_resize_image_big(rec.image_medium)
|
||||
|
||||
def _inverse_image_small(self):
|
||||
for rec in self:
|
||||
rec.image = tools.image_resize_image_big(rec.image_small)
|
||||
@@ -0,0 +1,324 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import uuid
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class pos_config(osv.osv):
|
||||
_name = 'pos.config'
|
||||
|
||||
POS_CONFIG_STATE = [
|
||||
('active', 'Active'),
|
||||
('inactive', 'Inactive'),
|
||||
('deprecated', 'Deprecated')
|
||||
]
|
||||
|
||||
def _get_currency(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict.fromkeys(ids, False)
|
||||
for pos_config in self.browse(cr, uid, ids, context=context):
|
||||
if pos_config.journal_id:
|
||||
currency_id = pos_config.journal_id.currency_id.id or pos_config.journal_id.company_id.currency_id.id
|
||||
else:
|
||||
currency_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id
|
||||
result[pos_config.id] = currency_id
|
||||
return result
|
||||
|
||||
def _get_current_session(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
session_id = record.session_ids.filtered(lambda r: r.user_id.id == uid and not r.state == 'closed')
|
||||
result[record.id] = {
|
||||
'current_session_id': session_id,
|
||||
'current_session_state': session_id.state,
|
||||
}
|
||||
return result
|
||||
|
||||
def _get_last_session(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
session_ids = self.pool['pos.session'].search_read(
|
||||
cr, uid,
|
||||
[('config_id', '=', record.id), ('state', '=', 'closed')],
|
||||
['cash_register_balance_end_real', 'stop_at'],
|
||||
order="stop_at desc", limit=1, context=context)
|
||||
if session_ids:
|
||||
result[record.id] = {
|
||||
'last_session_closing_cash': session_ids[0]['cash_register_balance_end_real'],
|
||||
'last_session_closing_date': session_ids[0]['stop_at'],
|
||||
}
|
||||
else:
|
||||
result[record.id] = {
|
||||
'last_session_closing_cash': 0,
|
||||
'last_session_closing_date': None,
|
||||
}
|
||||
return result
|
||||
|
||||
def _get_current_session_user(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
result[record.id] = record.session_ids.filtered(lambda r: r.state == 'opened').user_id.name
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'name' : fields.char('Point of Sale Name', select=1,
|
||||
required=True, help="An internal identification of the point of sale"),
|
||||
'journal_ids' : fields.many2many('account.journal', 'pos_config_journal_rel',
|
||||
'pos_config_id', 'journal_id', 'Available Payment Methods',
|
||||
domain="[('journal_user', '=', True ), ('type', 'in', ['bank', 'cash'])]",),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'),
|
||||
'stock_location_id': fields.many2one('stock.location', 'Stock Location', domain=[('usage', '=', 'internal')], required=True),
|
||||
'journal_id' : fields.many2one('account.journal', 'Sale Journal',
|
||||
domain=[('type', '=', 'sale')],
|
||||
help="Accounting journal used to post sales entries."),
|
||||
'currency_id' : fields.function(_get_currency, type="many2one", string="Currency", relation="res.currency"),
|
||||
'iface_cashdrawer' : fields.boolean('Cashdrawer', help="Automatically open the cashdrawer"),
|
||||
'iface_payment_terminal' : fields.boolean('Payment Terminal', help="Enables Payment Terminal integration"),
|
||||
'iface_electronic_scale' : fields.boolean('Electronic Scale', help="Enables Electronic Scale integration"),
|
||||
'iface_vkeyboard' : fields.boolean('Virtual KeyBoard', help="Enables an integrated Virtual Keyboard"),
|
||||
'iface_print_via_proxy' : fields.boolean('Print via Proxy', help="Bypass browser printing and prints via the hardware proxy"),
|
||||
'iface_scan_via_proxy' : fields.boolean('Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner"),
|
||||
'iface_invoicing': fields.boolean('Invoicing',help='Enables invoice generation from the Point of Sale'),
|
||||
'iface_big_scrollbars': fields.boolean('Large Scrollbars',help='For imprecise industrial touchscreens'),
|
||||
# TODO master: remove the `iface_fullscreen` field. This is no longer used.
|
||||
'iface_fullscreen': fields.boolean('Fullscreen', help='Display the Point of Sale in full screen mode'),
|
||||
'iface_print_auto': fields.boolean('Automatic Receipt Printing', help='The receipt will automatically be printed at the end of each order'),
|
||||
'iface_print_skip_screen': fields.boolean('Skip Receipt Screen', help='The receipt screen will be skipped if the receipt can be printed automatically.'),
|
||||
'iface_precompute_cash': fields.boolean('Prefill Cash Payment', help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount'),
|
||||
'iface_tax_included': fields.boolean('Include Taxes in Prices', help='The displayed prices will always include all taxes, even if the taxes have been setup differently'),
|
||||
'iface_start_categ_id': fields.many2one('pos.category','Start Category', help='The point of sale will display this product category by default. If no category is specified, all available products will be shown'),
|
||||
'iface_display_categ_images': fields.boolean('Display Category Pictures', help="The product categories will be displayed with pictures."),
|
||||
'cash_control': fields.boolean('Cash Control', help="Check the amount of the cashbox at opening and closing."),
|
||||
'receipt_header': fields.text('Receipt Header',help="A short text that will be inserted as a header in the printed receipt"),
|
||||
'receipt_footer': fields.text('Receipt Footer',help="A short text that will be inserted as a footer in the printed receipt"),
|
||||
'proxy_ip': fields.char('IP Address', help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty', size=45),
|
||||
|
||||
'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True, copy=False),
|
||||
'uuid' : fields.char('uuid', readonly=True, help='A globally unique identifier for this pos configuration, used to prevent conflicts in client-generated data'),
|
||||
'sequence_id' : fields.many2one('ir.sequence', 'Order IDs Sequence', readonly=True,
|
||||
help="This sequence is automatically created by Odoo but you can change it "\
|
||||
"to customize the reference numbers of your orders.", copy=False),
|
||||
'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'),
|
||||
'current_session_id': fields.function(_get_current_session, multi="session", type="many2one", relation="pos.session", string="Current Session"),
|
||||
'current_session_state': fields.function(_get_current_session, multi="session", type='char'),
|
||||
'last_session_closing_cash': fields.function(_get_last_session, multi="last_session", type='float'),
|
||||
'last_session_closing_date': fields.function(_get_last_session, multi="last_session", type='date'),
|
||||
'pos_session_username': fields.function(_get_current_session_user, type='char'),
|
||||
'group_by' : fields.boolean('Group Journal Items', help="Check this if you want to group the Journal Items by Product while closing a Session"),
|
||||
'pricelist_id': fields.many2one('product.pricelist','Pricelist', required=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True),
|
||||
'barcode_nomenclature_id': fields.many2one('barcode.nomenclature','Barcodes', help='Defines what kind of barcodes are available and how they are assigned to products, customers and cashiers', required=True),
|
||||
'group_pos_manager_id': fields.many2one('res.groups','Point of Sale Manager Group', help='This field is there to pass the id of the pos manager group to the point of sale client'),
|
||||
'group_pos_user_id': fields.many2one('res.groups','Point of Sale User Group', help='This field is there to pass the id of the pos user group to the point of sale client'),
|
||||
'tip_product_id': fields.many2one('product.product','Tip Product', help="The product used to encode the customer tip. Leave empty if you do not accept tips."),
|
||||
'fiscal_position_ids': fields.many2many('account.fiscal.position', string='Fiscal Positions')
|
||||
}
|
||||
|
||||
def _check_company_location(self, cr, uid, ids, context=None):
|
||||
for config in self.browse(cr, uid, ids, context=context):
|
||||
if config.stock_location_id.company_id and config.stock_location_id.company_id.id != config.company_id.id:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_company_journal(self, cr, uid, ids, context=None):
|
||||
for config in self.browse(cr, uid, ids, context=context):
|
||||
if config.journal_id and config.journal_id.company_id.id != config.company_id.id:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_company_payment(self, cr, uid, ids, context=None):
|
||||
for config in self.browse(cr, uid, ids, context=context):
|
||||
journal_ids = [j.id for j in config.journal_ids]
|
||||
if self.pool['account.journal'].search(cr, uid, [
|
||||
('id', 'in', journal_ids),
|
||||
('company_id', '!=', config.company_id.id)
|
||||
], count=True, context=context):
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_company_location, "The company of the stock location is different than the one of point of sale", ['company_id', 'stock_location_id']),
|
||||
(_check_company_journal, "The company of the sale journal is different than the one of point of sale", ['company_id', 'journal_id']),
|
||||
(_check_company_payment, "The company of a payment method is different than the one of point of sale", ['company_id', 'journal_ids']),
|
||||
]
|
||||
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
result = []
|
||||
states = {
|
||||
'opening_control': _('Opening Control'),
|
||||
'opened': _('In Progress'),
|
||||
'closing_control': _('Closing Control'),
|
||||
'closed': _('Closed & Posted'),
|
||||
}
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
if (not record.session_ids) or (record.session_ids[0].state=='closed'):
|
||||
result.append((record.id, record.name+' ('+_('not used')+')'))
|
||||
continue
|
||||
session = record.session_ids[0]
|
||||
result.append((record.id, record.name + ' ('+session.user_id.name+')')) #, '+states[session.state]+')'))
|
||||
return result
|
||||
|
||||
def _default_sale_journal(self, cr, uid, context=None):
|
||||
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
|
||||
res = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale'), ('company_id', '=', company_id)], limit=1, context=context)
|
||||
return res and res[0] or False
|
||||
|
||||
def _default_pricelist(self, cr, uid, context=None):
|
||||
res = self.pool.get('product.pricelist').search(cr, uid, [], limit=1, context=context)
|
||||
return res and res[0] or False
|
||||
|
||||
def _get_default_location(self, cr, uid, context=None):
|
||||
wh_obj = self.pool.get('stock.warehouse')
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||
res = wh_obj.search(cr, uid, [('company_id', '=', user.company_id.id)], limit=1, context=context)
|
||||
if res and res[0]:
|
||||
return wh_obj.browse(cr, uid, res[0], context=context).lot_stock_id.id
|
||||
return False
|
||||
|
||||
def _get_default_company(self, cr, uid, context=None):
|
||||
company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
|
||||
return company_id
|
||||
|
||||
def _get_default_nomenclature(self, cr, uid, context=None):
|
||||
nom_obj = self.pool.get('barcode.nomenclature')
|
||||
res = nom_obj.search(cr, uid, [], limit=1, context=context)
|
||||
return res and res[0] or False
|
||||
|
||||
def _get_group_pos_manager(self, cr, uid, context=None):
|
||||
group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_manager')
|
||||
if group:
|
||||
return group[1]
|
||||
else:
|
||||
return False
|
||||
|
||||
def _get_group_pos_user(self, cr, uid, context=None):
|
||||
group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_user')
|
||||
if group:
|
||||
return group[1]
|
||||
else:
|
||||
return False
|
||||
|
||||
_defaults = {
|
||||
'uuid' : lambda self, cr, uid, context={}: str(uuid.uuid4()),
|
||||
'state' : POS_CONFIG_STATE[0][0],
|
||||
'journal_id': _default_sale_journal,
|
||||
'group_by' : True,
|
||||
'pricelist_id': _default_pricelist,
|
||||
'iface_invoicing': True,
|
||||
'iface_print_auto': False,
|
||||
'iface_print_skip_screen': True,
|
||||
'stock_location_id': _get_default_location,
|
||||
'company_id': _get_default_company,
|
||||
'barcode_nomenclature_id': _get_default_nomenclature,
|
||||
'group_pos_manager_id': _get_group_pos_manager,
|
||||
'group_pos_user_id': _get_group_pos_user,
|
||||
}
|
||||
|
||||
def onchange_picking_type_id(self, cr, uid, ids, picking_type_id, context=None):
|
||||
p_type_obj = self.pool.get("stock.picking.type")
|
||||
p_type = p_type_obj.browse(cr, uid, picking_type_id, context=context)
|
||||
if p_type.default_location_src_id and p_type.default_location_src_id.usage == 'internal' and p_type.default_location_dest_id and p_type.default_location_dest_id.usage == 'customer':
|
||||
return {'value': {'stock_location_id': p_type.default_location_src_id.id}}
|
||||
return False
|
||||
|
||||
def onchange_iface_print_via_proxy(self, cr, uid, ids, print_via_proxy, context=None):
|
||||
return {'value': {'iface_print_auto': print_via_proxy}}
|
||||
|
||||
def set_active(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'active'}, context=context)
|
||||
|
||||
def set_inactive(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'inactive'}, context=context)
|
||||
|
||||
def set_deprecate(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'deprecated'}, context=context)
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
ir_sequence = self.pool.get('ir.sequence')
|
||||
# force sequence_id field to new pos.order sequence
|
||||
values['sequence_id'] = ir_sequence.create(cr, SUPERUSER_ID, {
|
||||
'name': 'POS Order %s' % values['name'],
|
||||
'padding': 4,
|
||||
'prefix': "%s/" % values['name'],
|
||||
'code': "pos.order",
|
||||
'company_id': values.get('company_id', False),
|
||||
}, context=context)
|
||||
|
||||
# TODO master: add field sequence_line_id on model
|
||||
# this make sure we always have one available per company
|
||||
ir_sequence.create(cr, SUPERUSER_ID, {
|
||||
'name': 'POS order line %s' % values['name'],
|
||||
'padding': 4,
|
||||
'prefix': "%s/" % values['name'],
|
||||
'code': "pos.order.line",
|
||||
'company_id': values.get('company_id', False),
|
||||
}, context=context)
|
||||
|
||||
return super(pos_config, self).create(cr, uid, values, context=context)
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.sequence_id:
|
||||
obj.sequence_id.unlink()
|
||||
return super(pos_config, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
# Methods to open the POS
|
||||
|
||||
def open_ui(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
context = dict(context or {})
|
||||
context['active_id'] = record.current_session_id.id
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': '/pos/web/',
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
def open_existing_session_cb_close(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
record.current_session_id.signal_workflow('cashbox_control')
|
||||
return self.open_session_cb(cr, uid, ids, context)
|
||||
|
||||
def open_session_cb(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
proxy = self.pool.get('pos.session')
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
current_session_id = record.current_session_id
|
||||
if not current_session_id:
|
||||
values = {
|
||||
'user_id': uid,
|
||||
'config_id': record.id,
|
||||
}
|
||||
session_id = proxy.create(cr, uid, values, context=context)
|
||||
self.write(cr, SUPERUSER_ID, record.id, {'current_session_id': session_id}, context=context)
|
||||
if record.current_session_id.state == 'opened':
|
||||
return self.open_ui(cr, uid, ids, context=context)
|
||||
return self._open_session(session_id)
|
||||
return self._open_session(current_session_id.id)
|
||||
|
||||
def open_existing_session_cb(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
return self._open_session(record.current_session_id.id)
|
||||
|
||||
def _open_session(self, session_id):
|
||||
return {
|
||||
'name': _('Session'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form,tree',
|
||||
'res_model': 'pos.session',
|
||||
'res_id': session_id,
|
||||
'view_id': False,
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
+2
-748
@@ -2,16 +2,15 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
import sets
|
||||
|
||||
from functools import partial
|
||||
|
||||
import openerp
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from openerp import tools, models, SUPERUSER_ID
|
||||
from openerp import tools, SUPERUSER_ID
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools import float_is_zero
|
||||
from openerp.tools.translate import _
|
||||
@@ -20,623 +19,6 @@ from openerp.exceptions import UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class pos_config(osv.osv):
|
||||
_name = 'pos.config'
|
||||
|
||||
POS_CONFIG_STATE = [
|
||||
('active', 'Active'),
|
||||
('inactive', 'Inactive'),
|
||||
('deprecated', 'Deprecated')
|
||||
]
|
||||
|
||||
def _get_currency(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict.fromkeys(ids, False)
|
||||
for pos_config in self.browse(cr, uid, ids, context=context):
|
||||
if pos_config.journal_id:
|
||||
currency_id = pos_config.journal_id.currency_id.id or pos_config.journal_id.company_id.currency_id.id
|
||||
else:
|
||||
currency_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id
|
||||
result[pos_config.id] = currency_id
|
||||
return result
|
||||
|
||||
def _get_current_session(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
session_id = record.session_ids.filtered(lambda r: r.user_id.id == uid and not r.state == 'closed')
|
||||
result[record.id] = {
|
||||
'current_session_id': session_id,
|
||||
'current_session_state': session_id.state,
|
||||
}
|
||||
return result
|
||||
|
||||
def _get_last_session(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
session_ids = self.pool['pos.session'].search_read(
|
||||
cr, uid,
|
||||
[('config_id', '=', record.id), ('state', '=', 'closed')],
|
||||
['cash_register_balance_end_real', 'stop_at'],
|
||||
order="stop_at desc", limit=1, context=context)
|
||||
if session_ids:
|
||||
result[record.id] = {
|
||||
'last_session_closing_cash': session_ids[0]['cash_register_balance_end_real'],
|
||||
'last_session_closing_date': session_ids[0]['stop_at'],
|
||||
}
|
||||
else:
|
||||
result[record.id] = {
|
||||
'last_session_closing_cash': 0,
|
||||
'last_session_closing_date': None,
|
||||
}
|
||||
return result
|
||||
|
||||
def _get_current_session_user(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
result[record.id] = record.session_ids.filtered(lambda r: r.state == 'opened').user_id.name
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'name' : fields.char('Point of Sale Name', select=1,
|
||||
required=True, help="An internal identification of the point of sale"),
|
||||
'journal_ids' : fields.many2many('account.journal', 'pos_config_journal_rel',
|
||||
'pos_config_id', 'journal_id', 'Available Payment Methods',
|
||||
domain="[('journal_user', '=', True ), ('type', 'in', ['bank', 'cash'])]",),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type'),
|
||||
'stock_location_id': fields.many2one('stock.location', 'Stock Location', domain=[('usage', '=', 'internal')], required=True),
|
||||
'journal_id' : fields.many2one('account.journal', 'Sale Journal',
|
||||
domain=[('type', '=', 'sale')],
|
||||
help="Accounting journal used to post sales entries."),
|
||||
'currency_id' : fields.function(_get_currency, type="many2one", string="Currency", relation="res.currency"),
|
||||
'iface_cashdrawer' : fields.boolean('Cashdrawer', help="Automatically open the cashdrawer"),
|
||||
'iface_payment_terminal' : fields.boolean('Payment Terminal', help="Enables Payment Terminal integration"),
|
||||
'iface_electronic_scale' : fields.boolean('Electronic Scale', help="Enables Electronic Scale integration"),
|
||||
'iface_vkeyboard' : fields.boolean('Virtual KeyBoard', help="Enables an integrated Virtual Keyboard"),
|
||||
'iface_print_via_proxy' : fields.boolean('Print via Proxy', help="Bypass browser printing and prints via the hardware proxy"),
|
||||
'iface_scan_via_proxy' : fields.boolean('Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner"),
|
||||
'iface_invoicing': fields.boolean('Invoicing',help='Enables invoice generation from the Point of Sale'),
|
||||
'iface_big_scrollbars': fields.boolean('Large Scrollbars',help='For imprecise industrial touchscreens'),
|
||||
# TODO master: remove the `iface_fullscreen` field. This is no longer used.
|
||||
'iface_fullscreen': fields.boolean('Fullscreen', help='Display the Point of Sale in full screen mode'),
|
||||
'iface_print_auto': fields.boolean('Automatic Receipt Printing', help='The receipt will automatically be printed at the end of each order'),
|
||||
'iface_print_skip_screen': fields.boolean('Skip Receipt Screen', help='The receipt screen will be skipped if the receipt can be printed automatically.'),
|
||||
'iface_precompute_cash': fields.boolean('Prefill Cash Payment', help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount'),
|
||||
'iface_tax_included': fields.boolean('Include Taxes in Prices', help='The displayed prices will always include all taxes, even if the taxes have been setup differently'),
|
||||
'iface_start_categ_id': fields.many2one('pos.category','Start Category', help='The point of sale will display this product category by default. If no category is specified, all available products will be shown'),
|
||||
'iface_display_categ_images': fields.boolean('Display Category Pictures', help="The product categories will be displayed with pictures."),
|
||||
'cash_control': fields.boolean('Cash Control', help="Check the amount of the cashbox at opening and closing."),
|
||||
'receipt_header': fields.text('Receipt Header',help="A short text that will be inserted as a header in the printed receipt"),
|
||||
'receipt_footer': fields.text('Receipt Footer',help="A short text that will be inserted as a footer in the printed receipt"),
|
||||
'proxy_ip': fields.char('IP Address', help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty', size=45),
|
||||
|
||||
'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True, copy=False),
|
||||
'uuid' : fields.char('uuid', readonly=True, help='A globally unique identifier for this pos configuration, used to prevent conflicts in client-generated data'),
|
||||
'sequence_id' : fields.many2one('ir.sequence', 'Order IDs Sequence', readonly=True,
|
||||
help="This sequence is automatically created by Odoo but you can change it "\
|
||||
"to customize the reference numbers of your orders.", copy=False),
|
||||
'session_ids': fields.one2many('pos.session', 'config_id', 'Sessions'),
|
||||
'current_session_id': fields.function(_get_current_session, multi="session", type="many2one", relation="pos.session", string="Current Session"),
|
||||
'current_session_state': fields.function(_get_current_session, multi="session", type='char'),
|
||||
'last_session_closing_cash': fields.function(_get_last_session, multi="last_session", type='float'),
|
||||
'last_session_closing_date': fields.function(_get_last_session, multi="last_session", type='date'),
|
||||
'pos_session_username': fields.function(_get_current_session_user, type='char'),
|
||||
'group_by' : fields.boolean('Group Journal Items', help="Check this if you want to group the Journal Items by Product while closing a Session"),
|
||||
'pricelist_id': fields.many2one('product.pricelist','Pricelist', required=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True),
|
||||
'barcode_nomenclature_id': fields.many2one('barcode.nomenclature','Barcodes', help='Defines what kind of barcodes are available and how they are assigned to products, customers and cashiers', required=True),
|
||||
'group_pos_manager_id': fields.many2one('res.groups','Point of Sale Manager Group', help='This field is there to pass the id of the pos manager group to the point of sale client'),
|
||||
'group_pos_user_id': fields.many2one('res.groups','Point of Sale User Group', help='This field is there to pass the id of the pos user group to the point of sale client'),
|
||||
'tip_product_id': fields.many2one('product.product','Tip Product', help="The product used to encode the customer tip. Leave empty if you do not accept tips."),
|
||||
'fiscal_position_ids': fields.many2many('account.fiscal.position', string='Fiscal Positions')
|
||||
}
|
||||
|
||||
def _check_company_location(self, cr, uid, ids, context=None):
|
||||
for config in self.browse(cr, uid, ids, context=context):
|
||||
if config.stock_location_id.company_id and config.stock_location_id.company_id.id != config.company_id.id:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_company_journal(self, cr, uid, ids, context=None):
|
||||
for config in self.browse(cr, uid, ids, context=context):
|
||||
if config.journal_id and config.journal_id.company_id.id != config.company_id.id:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_company_payment(self, cr, uid, ids, context=None):
|
||||
for config in self.browse(cr, uid, ids, context=context):
|
||||
journal_ids = [j.id for j in config.journal_ids]
|
||||
if self.pool['account.journal'].search(cr, uid, [
|
||||
('id', 'in', journal_ids),
|
||||
('company_id', '!=', config.company_id.id)
|
||||
], count=True, context=context):
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_company_location, "The company of the stock location is different than the one of point of sale", ['company_id', 'stock_location_id']),
|
||||
(_check_company_journal, "The company of the sale journal is different than the one of point of sale", ['company_id', 'journal_id']),
|
||||
(_check_company_payment, "The company of a payment method is different than the one of point of sale", ['company_id', 'journal_ids']),
|
||||
]
|
||||
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
result = []
|
||||
states = {
|
||||
'opening_control': _('Opening Control'),
|
||||
'opened': _('In Progress'),
|
||||
'closing_control': _('Closing Control'),
|
||||
'closed': _('Closed & Posted'),
|
||||
}
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
if (not record.session_ids) or (record.session_ids[0].state=='closed'):
|
||||
result.append((record.id, record.name+' ('+_('not used')+')'))
|
||||
continue
|
||||
session = record.session_ids[0]
|
||||
result.append((record.id, record.name + ' ('+session.user_id.name+')')) #, '+states[session.state]+')'))
|
||||
return result
|
||||
|
||||
def _default_sale_journal(self, cr, uid, context=None):
|
||||
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
|
||||
res = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale'), ('company_id', '=', company_id)], limit=1, context=context)
|
||||
return res and res[0] or False
|
||||
|
||||
def _default_pricelist(self, cr, uid, context=None):
|
||||
res = self.pool.get('product.pricelist').search(cr, uid, [], limit=1, context=context)
|
||||
return res and res[0] or False
|
||||
|
||||
def _get_default_location(self, cr, uid, context=None):
|
||||
wh_obj = self.pool.get('stock.warehouse')
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||
res = wh_obj.search(cr, uid, [('company_id', '=', user.company_id.id)], limit=1, context=context)
|
||||
if res and res[0]:
|
||||
return wh_obj.browse(cr, uid, res[0], context=context).lot_stock_id.id
|
||||
return False
|
||||
|
||||
def _get_default_company(self, cr, uid, context=None):
|
||||
company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
|
||||
return company_id
|
||||
|
||||
def _get_default_nomenclature(self, cr, uid, context=None):
|
||||
nom_obj = self.pool.get('barcode.nomenclature')
|
||||
res = nom_obj.search(cr, uid, [], limit=1, context=context)
|
||||
return res and res[0] or False
|
||||
|
||||
def _get_group_pos_manager(self, cr, uid, context=None):
|
||||
group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_manager')
|
||||
if group:
|
||||
return group[1]
|
||||
else:
|
||||
return False
|
||||
|
||||
def _get_group_pos_user(self, cr, uid, context=None):
|
||||
group = self.pool.get('ir.model.data').get_object_reference(cr,uid,'point_of_sale','group_pos_user')
|
||||
if group:
|
||||
return group[1]
|
||||
else:
|
||||
return False
|
||||
|
||||
_defaults = {
|
||||
'uuid' : lambda self, cr, uid, context={}: str(uuid.uuid4()),
|
||||
'state' : POS_CONFIG_STATE[0][0],
|
||||
'journal_id': _default_sale_journal,
|
||||
'group_by' : True,
|
||||
'pricelist_id': _default_pricelist,
|
||||
'iface_invoicing': True,
|
||||
'iface_print_auto': False,
|
||||
'iface_print_skip_screen': True,
|
||||
'stock_location_id': _get_default_location,
|
||||
'company_id': _get_default_company,
|
||||
'barcode_nomenclature_id': _get_default_nomenclature,
|
||||
'group_pos_manager_id': _get_group_pos_manager,
|
||||
'group_pos_user_id': _get_group_pos_user,
|
||||
}
|
||||
|
||||
def onchange_picking_type_id(self, cr, uid, ids, picking_type_id, context=None):
|
||||
p_type_obj = self.pool.get("stock.picking.type")
|
||||
p_type = p_type_obj.browse(cr, uid, picking_type_id, context=context)
|
||||
if p_type.default_location_src_id and p_type.default_location_src_id.usage == 'internal' and p_type.default_location_dest_id and p_type.default_location_dest_id.usage == 'customer':
|
||||
return {'value': {'stock_location_id': p_type.default_location_src_id.id}}
|
||||
return False
|
||||
|
||||
def onchange_iface_print_via_proxy(self, cr, uid, ids, print_via_proxy, context=None):
|
||||
return {'value': {'iface_print_auto': print_via_proxy}}
|
||||
|
||||
def set_active(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'active'}, context=context)
|
||||
|
||||
def set_inactive(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'inactive'}, context=context)
|
||||
|
||||
def set_deprecate(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'deprecated'}, context=context)
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
ir_sequence = self.pool.get('ir.sequence')
|
||||
# force sequence_id field to new pos.order sequence
|
||||
values['sequence_id'] = ir_sequence.create(cr, SUPERUSER_ID, {
|
||||
'name': 'POS Order %s' % values['name'],
|
||||
'padding': 4,
|
||||
'prefix': "%s/" % values['name'],
|
||||
'code': "pos.order",
|
||||
'company_id': values.get('company_id', False),
|
||||
}, context=context)
|
||||
|
||||
# TODO master: add field sequence_line_id on model
|
||||
# this make sure we always have one available per company
|
||||
ir_sequence.create(cr, SUPERUSER_ID, {
|
||||
'name': 'POS order line %s' % values['name'],
|
||||
'padding': 4,
|
||||
'prefix': "%s/" % values['name'],
|
||||
'code': "pos.order.line",
|
||||
'company_id': values.get('company_id', False),
|
||||
}, context=context)
|
||||
|
||||
return super(pos_config, self).create(cr, uid, values, context=context)
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.sequence_id:
|
||||
obj.sequence_id.unlink()
|
||||
return super(pos_config, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
# Methods to open the POS
|
||||
|
||||
def open_ui(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
context = dict(context or {})
|
||||
context['active_id'] = record.current_session_id.id
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': '/pos/web/',
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
def open_existing_session_cb_close(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
record.current_session_id.signal_workflow('cashbox_control')
|
||||
return self.open_session_cb(cr, uid, ids, context)
|
||||
|
||||
def open_session_cb(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
proxy = self.pool.get('pos.session')
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
current_session_id = record.current_session_id
|
||||
if not current_session_id:
|
||||
values = {
|
||||
'user_id': uid,
|
||||
'config_id': record.id,
|
||||
}
|
||||
session_id = proxy.create(cr, uid, values, context=context)
|
||||
self.write(cr, SUPERUSER_ID, record.id, {'current_session_id': session_id}, context=context)
|
||||
if record.current_session_id.state == 'opened':
|
||||
return self.open_ui(cr, uid, ids, context=context)
|
||||
return self._open_session(session_id)
|
||||
return self._open_session(current_session_id.id)
|
||||
|
||||
def open_existing_session_cb(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "you can open only one session at a time"
|
||||
|
||||
record = self.browse(cr, uid, ids[0], context=context)
|
||||
return self._open_session(record.current_session_id.id)
|
||||
|
||||
def _open_session(self, session_id):
|
||||
return {
|
||||
'name': _('Session'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form,tree',
|
||||
'res_model': 'pos.session',
|
||||
'res_id': session_id,
|
||||
'view_id': False,
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
|
||||
class pos_session(osv.osv):
|
||||
_name = 'pos.session'
|
||||
_order = 'id desc'
|
||||
|
||||
POS_SESSION_STATE = [
|
||||
('opening_control', 'Opening Control'), # Signal open
|
||||
('opened', 'In Progress'), # Signal closing
|
||||
('closing_control', 'Closing Control'), # Signal close
|
||||
('closed', 'Closed & Posted'),
|
||||
]
|
||||
|
||||
def _compute_cash_all(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
result[record.id] = {
|
||||
'cash_journal_id' : False,
|
||||
'cash_register_id' : False,
|
||||
'cash_control' : False,
|
||||
}
|
||||
|
||||
if record.config_id.cash_control:
|
||||
for st in record.statement_ids:
|
||||
if st.journal_id.type == 'cash':
|
||||
result[record.id]['cash_control'] = True
|
||||
result[record.id]['cash_journal_id'] = st.journal_id.id
|
||||
result[record.id]['cash_register_id'] = st.id
|
||||
|
||||
if not result[record.id]['cash_control']:
|
||||
raise UserError(_("Cash control can only be applied to cash journals."))
|
||||
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'config_id' : fields.many2one('pos.config', 'Point of Sale',
|
||||
help="The physical point of sale you will use.",
|
||||
required=True,
|
||||
select=1,
|
||||
domain="[('state', '=', 'active')]",
|
||||
),
|
||||
|
||||
'name' : fields.char('Session ID', required=True, readonly=True),
|
||||
'user_id' : fields.many2one('res.users', 'Responsible',
|
||||
required=True,
|
||||
select=1,
|
||||
readonly=True,
|
||||
states={'opening_control' : [('readonly', False)]}
|
||||
),
|
||||
'currency_id' : fields.related('config_id', 'currency_id', type="many2one", relation='res.currency', string="Currency"),
|
||||
'start_at' : fields.datetime('Opening Date', readonly=True),
|
||||
'stop_at' : fields.datetime('Closing Date', readonly=True, copy=False),
|
||||
|
||||
'state' : fields.selection(POS_SESSION_STATE, 'Status',
|
||||
required=True, readonly=True,
|
||||
select=1, copy=False),
|
||||
|
||||
'sequence_number': fields.integer('Order Sequence Number', help='A sequence number that is incremented with each order'),
|
||||
'login_number': fields.integer('Login Sequence Number', help='A sequence number that is incremented each time a user resumes the pos session'),
|
||||
|
||||
'cash_control' : fields.function(_compute_cash_all,
|
||||
multi='cash',
|
||||
type='boolean', string='Has Cash Control'),
|
||||
'cash_journal_id' : fields.function(_compute_cash_all,
|
||||
multi='cash',
|
||||
type='many2one', relation='account.journal',
|
||||
string='Cash Journal', store=True),
|
||||
'cash_register_id' : fields.function(_compute_cash_all,
|
||||
multi='cash',
|
||||
type='many2one', relation='account.bank.statement',
|
||||
string='Cash Register', store=True),
|
||||
|
||||
'cash_register_balance_end_real' : fields.related('cash_register_id', 'balance_end_real',
|
||||
type='float',
|
||||
digits=0,
|
||||
string="Ending Balance",
|
||||
help="Total of closing cash control lines.",
|
||||
readonly=True),
|
||||
'cash_register_balance_start' : fields.related('cash_register_id', 'balance_start',
|
||||
type='float',
|
||||
digits=0,
|
||||
string="Starting Balance",
|
||||
help="Total of opening cash control lines.",
|
||||
readonly=True),
|
||||
'cash_register_total_entry_encoding' : fields.related('cash_register_id', 'total_entry_encoding',
|
||||
string='Total Cash Transaction',
|
||||
readonly=True,
|
||||
help="Total of all paid sale orders"),
|
||||
'cash_register_balance_end' : fields.related('cash_register_id', 'balance_end',
|
||||
type='float',
|
||||
digits=0,
|
||||
string="Theoretical Closing Balance",
|
||||
help="Sum of opening balance and transactions.",
|
||||
readonly=True),
|
||||
'cash_register_difference' : fields.related('cash_register_id', 'difference',
|
||||
type='float',
|
||||
string='Difference',
|
||||
help="Difference between the theoretical closing balance and the real closing balance.",
|
||||
readonly=True),
|
||||
|
||||
'journal_ids' : fields.related('config_id', 'journal_ids',
|
||||
type='many2many',
|
||||
readonly=True,
|
||||
relation='account.journal',
|
||||
string='Available Payment Methods'),
|
||||
'order_ids' : fields.one2many('pos.order', 'session_id', 'Orders'),
|
||||
|
||||
'statement_ids' : fields.one2many('account.bank.statement', 'pos_session_id', 'Bank Statement', readonly=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'name' : '/',
|
||||
'user_id' : lambda obj, cr, uid, context: uid,
|
||||
'state' : 'opening_control',
|
||||
'sequence_number': 1,
|
||||
'login_number': 0,
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('uniq_name', 'unique(name)', "The name of this POS Session must be unique !"),
|
||||
]
|
||||
|
||||
def _check_unicity(self, cr, uid, ids, context=None):
|
||||
for session in self.browse(cr, uid, ids, context=None):
|
||||
# open if there is no session in 'opening_control', 'opened', 'closing_control' for one user
|
||||
domain = [
|
||||
('state', 'not in', ('closed','closing_control')),
|
||||
('user_id', '=', session.user_id.id)
|
||||
]
|
||||
count = self.search_count(cr, uid, domain, context=context)
|
||||
if count>1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_pos_config(self, cr, uid, ids, context=None):
|
||||
for session in self.browse(cr, uid, ids, context=None):
|
||||
domain = [
|
||||
('state', '!=', 'closed'),
|
||||
('config_id', '=', session.config_id.id)
|
||||
]
|
||||
count = self.search_count(cr, uid, domain, context=context)
|
||||
if count>1:
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_unicity, "You cannot create two active sessions with the same responsible!", ['user_id', 'state']),
|
||||
(_check_pos_config, "You cannot create two active sessions related to the same point of sale!", ['config_id']),
|
||||
]
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
context = dict(context or {})
|
||||
config_id = values.get('config_id', False) or context.get('default_config_id', False)
|
||||
if not config_id:
|
||||
raise UserError(_("You should assign a Point of Sale to your session."))
|
||||
|
||||
# journal_id is not required on the pos_config because it does not
|
||||
# exists at the installation. If nothing is configured at the
|
||||
# installation we do the minimal configuration. Impossible to do in
|
||||
# the .xml files as the CoA is not yet installed.
|
||||
jobj = self.pool.get('pos.config')
|
||||
pos_config = jobj.browse(cr, uid, config_id, context=context)
|
||||
context.update({'company_id': pos_config.company_id.id})
|
||||
if not pos_config.journal_id:
|
||||
jid = jobj.default_get(cr, uid, ['journal_id'], context=context)['journal_id']
|
||||
if jid:
|
||||
jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_id': jid}, context=context)
|
||||
else:
|
||||
raise UserError(_("Unable to open the session. You have to assign a sale journal to your point of sale."))
|
||||
|
||||
# define some cash journal if no payment method exists
|
||||
if not pos_config.journal_ids:
|
||||
journal_proxy = self.pool.get('account.journal')
|
||||
cashids = journal_proxy.search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context)
|
||||
if not cashids:
|
||||
cashids = journal_proxy.search(cr, uid, [('type', '=', 'cash')], context=context)
|
||||
if not cashids:
|
||||
cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context)
|
||||
|
||||
journal_proxy.write(cr, SUPERUSER_ID, cashids, {'journal_user': True})
|
||||
jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
|
||||
|
||||
|
||||
pos_config = jobj.browse(cr, uid, config_id, context=context)
|
||||
|
||||
statements = [(0, 0, {
|
||||
'journal_id': journal.id,
|
||||
'user_id': uid,
|
||||
'company_id': pos_config.company_id.id
|
||||
}) for journal in pos_config.journal_ids]
|
||||
|
||||
values.update({
|
||||
'name': self.pool['ir.sequence'].next_by_code(cr, uid, 'pos.session', context=context),
|
||||
'statement_ids': statements,
|
||||
'config_id': config_id
|
||||
})
|
||||
|
||||
# set the journal_id which should be used by
|
||||
# account.bank.statement to set the opening balance of the
|
||||
# newly created bank statement
|
||||
if pos_config.cash_control:
|
||||
for journal in pos_config.journal_ids:
|
||||
if journal.type == 'cash':
|
||||
context.update({'journal_id': journal.id})
|
||||
|
||||
return super(pos_session, self).create(cr, uid, values, context=context)
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
self.pool.get('account.bank.statement').unlink(cr, uid, obj.statement_ids.ids, context=context)
|
||||
return super(pos_session, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def login(self, cr, uid, ids, context=None):
|
||||
this_record = self.browse(cr, uid, ids[0], context=context)
|
||||
this_record.write({
|
||||
'login_number': this_record.login_number+1,
|
||||
})
|
||||
|
||||
def wkf_action_open(self, cr, uid, ids, context=None):
|
||||
# second browse because we need to refetch the data from the DB for cash_register_id
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
values = {}
|
||||
if not record.start_at:
|
||||
values['start_at'] = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
values['state'] = 'opened'
|
||||
record.write(values)
|
||||
for st in record.statement_ids:
|
||||
st.button_open()
|
||||
|
||||
return True
|
||||
|
||||
def wkf_action_opening_control(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'opening_control'}, context=context)
|
||||
|
||||
def wkf_action_closing_control(self, cr, uid, ids, context=None):
|
||||
for session in self.browse(cr, uid, ids, context=context):
|
||||
for statement in session.statement_ids:
|
||||
if (statement != session.cash_register_id) and (statement.balance_end != statement.balance_end_real):
|
||||
self.pool.get('account.bank.statement').write(cr, uid, [statement.id], {'balance_end_real': statement.balance_end})
|
||||
return self.write(cr, uid, ids, {'state' : 'closing_control', 'stop_at' : time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
|
||||
|
||||
def wkf_action_close(self, cr, uid, ids, context=None):
|
||||
# Close CashBox
|
||||
local_context = dict(context)
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
company_id = record.config_id.company_id.id
|
||||
local_context.update({'force_company': company_id, 'company_id': company_id})
|
||||
for st in record.statement_ids:
|
||||
if abs(st.difference) > st.journal_id.amount_authorized_diff:
|
||||
# The pos manager can close statements with maximums.
|
||||
if not self.pool.get('ir.model.access').check_groups(cr, uid, "point_of_sale.group_pos_manager"):
|
||||
raise UserError(_("Your ending balance is too different from the theoretical cash closing (%.2f), the maximum allowed is: %.2f. You can contact your manager to force it.") % (st.difference, st.journal_id.amount_authorized_diff))
|
||||
if (st.journal_id.type not in ['bank', 'cash']):
|
||||
raise UserError(_("The type of the journal for your payment method should be bank or cash "))
|
||||
self.pool['account.bank.statement'].button_confirm_bank(cr, SUPERUSER_ID, [st.id], context=local_context)
|
||||
self._confirm_orders(cr, uid, ids, context=local_context)
|
||||
self.write(cr, uid, ids, {'state' : 'closed'}, context=local_context)
|
||||
|
||||
obj = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'point_of_sale', 'menu_point_root')[1]
|
||||
return {
|
||||
'type' : 'ir.actions.client',
|
||||
'name' : 'Point of Sale Menu',
|
||||
'tag' : 'reload',
|
||||
'params' : {'menu_id': obj},
|
||||
}
|
||||
|
||||
def _confirm_orders(self, cr, uid, ids, context=None):
|
||||
pos_order_obj = self.pool.get('pos.order')
|
||||
for session in self.browse(cr, uid, ids, context=context):
|
||||
company_id = session.config_id.journal_id.company_id.id
|
||||
local_context = dict(context or {}, force_company=company_id)
|
||||
order_ids = [order.id for order in session.order_ids if order.state == 'paid']
|
||||
|
||||
move_id = pos_order_obj._create_account_move(cr, uid, session.start_at, session.name, session.config_id.journal_id.id, company_id, context=context)
|
||||
|
||||
pos_order_obj._create_account_move_line(cr, uid, order_ids, session, move_id, context=local_context)
|
||||
|
||||
for order in session.order_ids:
|
||||
if order.state == 'done':
|
||||
continue
|
||||
if order.state not in ('paid', 'invoiced'):
|
||||
raise UserError(_("You cannot confirm all orders of this session, because they have not the 'paid' status"))
|
||||
else:
|
||||
pos_order_obj.signal_workflow(cr, uid, [order.id], 'done')
|
||||
|
||||
return True
|
||||
|
||||
def open_frontend_cb(self, cr, uid, ids, context=None):
|
||||
if not context:
|
||||
context = {}
|
||||
if not ids:
|
||||
return {}
|
||||
for session in self.browse(cr, uid, ids, context=context):
|
||||
if session.user_id.id != uid:
|
||||
raise UserError(_("You cannot use the session of another users. This session is owned by %s. "
|
||||
"Please first close this one to use this point of sale.") % session.user_id.name)
|
||||
context.update({'active_id': ids[0]})
|
||||
return {
|
||||
'type' : 'ir.actions.act_url',
|
||||
'target': 'self',
|
||||
'url': '/pos/web/',
|
||||
}
|
||||
|
||||
class pos_order(osv.osv):
|
||||
_name = "pos.order"
|
||||
@@ -1377,12 +759,6 @@ class pos_order(osv.osv):
|
||||
self.create_account_move(cr, uid, ids, context=context)
|
||||
return True
|
||||
|
||||
class account_bank_statement_line(osv.osv):
|
||||
_inherit = 'account.bank.statement.line'
|
||||
_columns= {
|
||||
'pos_statement_id': fields.many2one('pos.order', string="POS statement", ondelete='cascade'),
|
||||
}
|
||||
|
||||
class pos_order_line(osv.osv):
|
||||
_name = "pos.order.line"
|
||||
_description = "Lines of Point of Sale"
|
||||
@@ -1480,125 +856,3 @@ class pos_order_line(osv.osv):
|
||||
'discount': lambda *a: 0.0,
|
||||
'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
|
||||
}
|
||||
|
||||
class pos_category(osv.osv):
|
||||
_name = "pos.category"
|
||||
_description = "Public Category"
|
||||
_order = "sequence, name"
|
||||
|
||||
_constraints = [
|
||||
(osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
|
||||
]
|
||||
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
res = []
|
||||
for cat in self.browse(cr, uid, ids, context=context):
|
||||
names = [cat.name]
|
||||
pcat = cat.parent_id
|
||||
while pcat:
|
||||
names.append(pcat.name)
|
||||
pcat = pcat.parent_id
|
||||
res.append((cat.id, ' / '.join(reversed(names))))
|
||||
return res
|
||||
|
||||
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
|
||||
res = self.name_get(cr, uid, ids, context=context)
|
||||
return dict(res)
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True, translate=True),
|
||||
'complete_name': fields.function(_name_get_fnc, type="char", string='Name'),
|
||||
'parent_id': fields.many2one('pos.category','Parent Category', select=True),
|
||||
'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
|
||||
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
|
||||
|
||||
}
|
||||
|
||||
# NOTE: there is no 'default image', because by default we don't show
|
||||
# thumbnails for categories. However if we have a thumbnail for at least one
|
||||
# category, then we display a default image on the other, so that the
|
||||
# buttons have consistent styling.
|
||||
# In this case, the default image is set by the js code.
|
||||
image = openerp.fields.Binary("Image", attachment=True,
|
||||
help="This field holds the image used as image for the cateogry, limited to 1024x1024px.")
|
||||
image_medium = openerp.fields.Binary("Medium-sized image",
|
||||
compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
|
||||
help="Medium-sized image of the category. It is automatically "\
|
||||
"resized as a 128x128px image, with aspect ratio preserved. "\
|
||||
"Use this field in form views or some kanban views.")
|
||||
image_small = openerp.fields.Binary("Small-sized image",
|
||||
compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
|
||||
help="Small-sized image of the category. It is automatically "\
|
||||
"resized as a 64x64px image, with aspect ratio preserved. "\
|
||||
"Use this field anywhere a small image is required.")
|
||||
|
||||
@openerp.api.depends('image')
|
||||
def _compute_images(self):
|
||||
for rec in self:
|
||||
rec.image_medium = tools.image_resize_image_medium(rec.image)
|
||||
rec.image_small = tools.image_resize_image_small(rec.image)
|
||||
|
||||
def _inverse_image_medium(self):
|
||||
for rec in self:
|
||||
rec.image = tools.image_resize_image_big(rec.image_medium)
|
||||
|
||||
def _inverse_image_small(self):
|
||||
for rec in self:
|
||||
rec.image = tools.image_resize_image_big(rec.image_small)
|
||||
|
||||
class product_template(osv.osv):
|
||||
_inherit = 'product.template'
|
||||
|
||||
_columns = {
|
||||
'available_in_pos': fields.boolean('Available in the Point of Sale', help='Check if you want this product to appear in the Point of Sale'),
|
||||
'to_weight' : fields.boolean('To Weigh With Scale', help="Check if the product should be weighted using the hardware scale integration"),
|
||||
'pos_categ_id': fields.many2one('pos.category','Point of Sale Category', help="Those categories are used to group similar products for point of sale."),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'to_weight' : False,
|
||||
'available_in_pos': True,
|
||||
}
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
product_ctx = dict(context or {}, active_test=False)
|
||||
if self.search_count(cr, uid, [('id', 'in', ids), ('available_in_pos', '=', True)], context=product_ctx):
|
||||
if self.pool['pos.session'].search_count(cr, uid, [('state', '!=', 'closed')], context=context):
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('You cannot delete a product saleable in point of sale while a session is still opened.'))
|
||||
return super(product_template, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
class res_partner(osv.osv):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
def create_from_ui(self, cr, uid, partner, context=None):
|
||||
""" create or modify a partner from the point of sale ui.
|
||||
partner contains the partner's fields. """
|
||||
|
||||
#image is a dataurl, get the data after the comma
|
||||
if partner.get('image',False):
|
||||
img = partner['image'].split(',')[1]
|
||||
partner['image'] = img
|
||||
|
||||
if partner.get('id',False): # Modifying existing partner
|
||||
partner_id = partner['id']
|
||||
del partner['id']
|
||||
self.write(cr, uid, [partner_id], partner, context=context)
|
||||
else:
|
||||
partner_id = self.create(cr, uid, partner, context=context)
|
||||
|
||||
return partner_id
|
||||
|
||||
class barcode_rule(models.Model):
|
||||
_inherit = 'barcode.rule'
|
||||
|
||||
def _get_type_selection(self):
|
||||
types = sets.Set(super(barcode_rule,self)._get_type_selection())
|
||||
types.update([
|
||||
('weight', _('Weighted Product')),
|
||||
('price', _('Priced Product')),
|
||||
('discount', _('Discounted Product')),
|
||||
('client', _('Client')),
|
||||
('cashier', _('Cashier'))
|
||||
])
|
||||
return list(types)
|
||||
@@ -0,0 +1,307 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import time
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class pos_session(osv.osv):
|
||||
_name = 'pos.session'
|
||||
_order = 'id desc'
|
||||
|
||||
POS_SESSION_STATE = [
|
||||
('opening_control', 'Opening Control'), # Signal open
|
||||
('opened', 'In Progress'), # Signal closing
|
||||
('closing_control', 'Closing Control'), # Signal close
|
||||
('closed', 'Closed & Posted'),
|
||||
]
|
||||
|
||||
def _compute_cash_all(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict()
|
||||
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
result[record.id] = {
|
||||
'cash_journal_id' : False,
|
||||
'cash_register_id' : False,
|
||||
'cash_control' : False,
|
||||
}
|
||||
|
||||
if record.config_id.cash_control:
|
||||
for st in record.statement_ids:
|
||||
if st.journal_id.type == 'cash':
|
||||
result[record.id]['cash_control'] = True
|
||||
result[record.id]['cash_journal_id'] = st.journal_id.id
|
||||
result[record.id]['cash_register_id'] = st.id
|
||||
|
||||
if not result[record.id]['cash_control']:
|
||||
raise UserError(_("Cash control can only be applied to cash journals."))
|
||||
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'config_id' : fields.many2one('pos.config', 'Point of Sale',
|
||||
help="The physical point of sale you will use.",
|
||||
required=True,
|
||||
select=1,
|
||||
domain="[('state', '=', 'active')]",
|
||||
),
|
||||
|
||||
'name' : fields.char('Session ID', required=True, readonly=True),
|
||||
'user_id' : fields.many2one('res.users', 'Responsible',
|
||||
required=True,
|
||||
select=1,
|
||||
readonly=True,
|
||||
states={'opening_control' : [('readonly', False)]}
|
||||
),
|
||||
'currency_id' : fields.related('config_id', 'currency_id', type="many2one", relation='res.currency', string="Currency"),
|
||||
'start_at' : fields.datetime('Opening Date', readonly=True),
|
||||
'stop_at' : fields.datetime('Closing Date', readonly=True, copy=False),
|
||||
|
||||
'state' : fields.selection(POS_SESSION_STATE, 'Status',
|
||||
required=True, readonly=True,
|
||||
select=1, copy=False),
|
||||
|
||||
'sequence_number': fields.integer('Order Sequence Number', help='A sequence number that is incremented with each order'),
|
||||
'login_number': fields.integer('Login Sequence Number', help='A sequence number that is incremented each time a user resumes the pos session'),
|
||||
|
||||
'cash_control' : fields.function(_compute_cash_all,
|
||||
multi='cash',
|
||||
type='boolean', string='Has Cash Control'),
|
||||
'cash_journal_id' : fields.function(_compute_cash_all,
|
||||
multi='cash',
|
||||
type='many2one', relation='account.journal',
|
||||
string='Cash Journal', store=True),
|
||||
'cash_register_id' : fields.function(_compute_cash_all,
|
||||
multi='cash',
|
||||
type='many2one', relation='account.bank.statement',
|
||||
string='Cash Register', store=True),
|
||||
|
||||
'cash_register_balance_end_real' : fields.related('cash_register_id', 'balance_end_real',
|
||||
type='float',
|
||||
digits=0,
|
||||
string="Ending Balance",
|
||||
help="Total of closing cash control lines.",
|
||||
readonly=True),
|
||||
'cash_register_balance_start' : fields.related('cash_register_id', 'balance_start',
|
||||
type='float',
|
||||
digits=0,
|
||||
string="Starting Balance",
|
||||
help="Total of opening cash control lines.",
|
||||
readonly=True),
|
||||
'cash_register_total_entry_encoding' : fields.related('cash_register_id', 'total_entry_encoding',
|
||||
string='Total Cash Transaction',
|
||||
readonly=True,
|
||||
help="Total of all paid sale orders"),
|
||||
'cash_register_balance_end' : fields.related('cash_register_id', 'balance_end',
|
||||
type='float',
|
||||
digits=0,
|
||||
string="Theoretical Closing Balance",
|
||||
help="Sum of opening balance and transactions.",
|
||||
readonly=True),
|
||||
'cash_register_difference' : fields.related('cash_register_id', 'difference',
|
||||
type='float',
|
||||
string='Difference',
|
||||
help="Difference between the theoretical closing balance and the real closing balance.",
|
||||
readonly=True),
|
||||
|
||||
'journal_ids' : fields.related('config_id', 'journal_ids',
|
||||
type='many2many',
|
||||
readonly=True,
|
||||
relation='account.journal',
|
||||
string='Available Payment Methods'),
|
||||
'order_ids' : fields.one2many('pos.order', 'session_id', 'Orders'),
|
||||
|
||||
'statement_ids' : fields.one2many('account.bank.statement', 'pos_session_id', 'Bank Statement', readonly=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'name' : '/',
|
||||
'user_id' : lambda obj, cr, uid, context: uid,
|
||||
'state' : 'opening_control',
|
||||
'sequence_number': 1,
|
||||
'login_number': 0,
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('uniq_name', 'unique(name)', "The name of this POS Session must be unique !"),
|
||||
]
|
||||
|
||||
def _check_unicity(self, cr, uid, ids, context=None):
|
||||
for session in self.browse(cr, uid, ids, context=None):
|
||||
# open if there is no session in 'opening_control', 'opened', 'closing_control' for one user
|
||||
domain = [
|
||||
('state', 'not in', ('closed','closing_control')),
|
||||
('user_id', '=', session.user_id.id)
|
||||
]
|
||||
count = self.search_count(cr, uid, domain, context=context)
|
||||
if count>1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_pos_config(self, cr, uid, ids, context=None):
|
||||
for session in self.browse(cr, uid, ids, context=None):
|
||||
domain = [
|
||||
('state', '!=', 'closed'),
|
||||
('config_id', '=', session.config_id.id)
|
||||
]
|
||||
count = self.search_count(cr, uid, domain, context=context)
|
||||
if count>1:
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_unicity, "You cannot create two active sessions with the same responsible!", ['user_id', 'state']),
|
||||
(_check_pos_config, "You cannot create two active sessions related to the same point of sale!", ['config_id']),
|
||||
]
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
context = dict(context or {})
|
||||
config_id = values.get('config_id', False) or context.get('default_config_id', False)
|
||||
if not config_id:
|
||||
raise UserError(_("You should assign a Point of Sale to your session."))
|
||||
|
||||
# journal_id is not required on the pos_config because it does not
|
||||
# exists at the installation. If nothing is configured at the
|
||||
# installation we do the minimal configuration. Impossible to do in
|
||||
# the .xml files as the CoA is not yet installed.
|
||||
jobj = self.pool.get('pos.config')
|
||||
pos_config = jobj.browse(cr, uid, config_id, context=context)
|
||||
context.update({'company_id': pos_config.company_id.id})
|
||||
if not pos_config.journal_id:
|
||||
jid = jobj.default_get(cr, uid, ['journal_id'], context=context)['journal_id']
|
||||
if jid:
|
||||
jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_id': jid}, context=context)
|
||||
else:
|
||||
raise UserError(_("Unable to open the session. You have to assign a sale journal to your point of sale."))
|
||||
|
||||
# define some cash journal if no payment method exists
|
||||
if not pos_config.journal_ids:
|
||||
journal_proxy = self.pool.get('account.journal')
|
||||
cashids = journal_proxy.search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context)
|
||||
if not cashids:
|
||||
cashids = journal_proxy.search(cr, uid, [('type', '=', 'cash')], context=context)
|
||||
if not cashids:
|
||||
cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context)
|
||||
|
||||
journal_proxy.write(cr, SUPERUSER_ID, cashids, {'journal_user': True})
|
||||
jobj.write(cr, SUPERUSER_ID, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
|
||||
|
||||
|
||||
pos_config = jobj.browse(cr, uid, config_id, context=context)
|
||||
|
||||
statements = [(0, 0, {
|
||||
'journal_id': journal.id,
|
||||
'user_id': uid,
|
||||
'company_id': pos_config.company_id.id
|
||||
}) for journal in pos_config.journal_ids]
|
||||
|
||||
values.update({
|
||||
'name': self.pool['ir.sequence'].next_by_code(cr, uid, 'pos.session', context=context),
|
||||
'statement_ids': statements,
|
||||
'config_id': config_id
|
||||
})
|
||||
|
||||
# set the journal_id which should be used by
|
||||
# account.bank.statement to set the opening balance of the
|
||||
# newly created bank statement
|
||||
if pos_config.cash_control:
|
||||
for journal in pos_config.journal_ids:
|
||||
if journal.type == 'cash':
|
||||
context.update({'journal_id': journal.id})
|
||||
|
||||
return super(pos_session, self).create(cr, uid, values, context=context)
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
self.pool.get('account.bank.statement').unlink(cr, uid, obj.statement_ids.ids, context=context)
|
||||
return super(pos_session, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def login(self, cr, uid, ids, context=None):
|
||||
this_record = self.browse(cr, uid, ids[0], context=context)
|
||||
this_record.write({
|
||||
'login_number': this_record.login_number+1,
|
||||
})
|
||||
|
||||
def wkf_action_open(self, cr, uid, ids, context=None):
|
||||
# second browse because we need to refetch the data from the DB for cash_register_id
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
values = {}
|
||||
if not record.start_at:
|
||||
values['start_at'] = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
values['state'] = 'opened'
|
||||
record.write(values)
|
||||
for st in record.statement_ids:
|
||||
st.button_open()
|
||||
|
||||
return True
|
||||
|
||||
def wkf_action_opening_control(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state' : 'opening_control'}, context=context)
|
||||
|
||||
def wkf_action_closing_control(self, cr, uid, ids, context=None):
|
||||
for session in self.browse(cr, uid, ids, context=context):
|
||||
for statement in session.statement_ids:
|
||||
if (statement != session.cash_register_id) and (statement.balance_end != statement.balance_end_real):
|
||||
self.pool.get('account.bank.statement').write(cr, uid, [statement.id], {'balance_end_real': statement.balance_end})
|
||||
return self.write(cr, uid, ids, {'state' : 'closing_control', 'stop_at' : time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
|
||||
|
||||
def wkf_action_close(self, cr, uid, ids, context=None):
|
||||
# Close CashBox
|
||||
local_context = dict(context)
|
||||
for record in self.browse(cr, uid, ids, context=context):
|
||||
company_id = record.config_id.company_id.id
|
||||
local_context.update({'force_company': company_id, 'company_id': company_id})
|
||||
for st in record.statement_ids:
|
||||
if abs(st.difference) > st.journal_id.amount_authorized_diff:
|
||||
# The pos manager can close statements with maximums.
|
||||
if not self.pool.get('ir.model.access').check_groups(cr, uid, "point_of_sale.group_pos_manager"):
|
||||
raise UserError(_("Your ending balance is too different from the theoretical cash closing (%.2f), the maximum allowed is: %.2f. You can contact your manager to force it.") % (st.difference, st.journal_id.amount_authorized_diff))
|
||||
if (st.journal_id.type not in ['bank', 'cash']):
|
||||
raise UserError(_("The type of the journal for your payment method should be bank or cash "))
|
||||
self.pool['account.bank.statement'].button_confirm_bank(cr, SUPERUSER_ID, [st.id], context=local_context)
|
||||
self._confirm_orders(cr, uid, ids, context=local_context)
|
||||
self.write(cr, uid, ids, {'state' : 'closed'}, context=local_context)
|
||||
|
||||
obj = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'point_of_sale', 'menu_point_root')[1]
|
||||
return {
|
||||
'type' : 'ir.actions.client',
|
||||
'name' : 'Point of Sale Menu',
|
||||
'tag' : 'reload',
|
||||
'params' : {'menu_id': obj},
|
||||
}
|
||||
|
||||
def _confirm_orders(self, cr, uid, ids, context=None):
|
||||
pos_order_obj = self.pool.get('pos.order')
|
||||
for session in self.browse(cr, uid, ids, context=context):
|
||||
company_id = session.config_id.journal_id.company_id.id
|
||||
local_context = dict(context or {}, force_company=company_id)
|
||||
order_ids = [order.id for order in session.order_ids if order.state == 'paid']
|
||||
|
||||
move_id = pos_order_obj._create_account_move(cr, uid, session.start_at, session.name, session.config_id.journal_id.id, company_id, context=context)
|
||||
|
||||
pos_order_obj._create_account_move_line(cr, uid, order_ids, session, move_id, context=local_context)
|
||||
|
||||
for order in session.order_ids:
|
||||
if order.state == 'done':
|
||||
continue
|
||||
if order.state not in ('paid', 'invoiced'):
|
||||
raise UserError(_("You cannot confirm all orders of this session, because they have not the 'paid' status"))
|
||||
else:
|
||||
pos_order_obj.signal_workflow(cr, uid, [order.id], 'done')
|
||||
|
||||
return True
|
||||
|
||||
def open_frontend_cb(self, cr, uid, ids, context=None):
|
||||
if not context:
|
||||
context = {}
|
||||
if not ids:
|
||||
return {}
|
||||
for session in self.browse(cr, uid, ids, context=context):
|
||||
if session.user_id.id != uid:
|
||||
raise UserError(_("You cannot use the session of another users. This session is owned by %s. "
|
||||
"Please first close this one to use this point of sale.") % session.user_id.name)
|
||||
context.update({'active_id': ids[0]})
|
||||
return {
|
||||
'type' : 'ir.actions.act_url',
|
||||
'target': 'self',
|
||||
'url': '/pos/web/',
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class product_template(osv.osv):
|
||||
_inherit = 'product.template'
|
||||
|
||||
_columns = {
|
||||
'available_in_pos': fields.boolean('Available in the Point of Sale', help='Check if you want this product to appear in the Point of Sale'),
|
||||
'to_weight' : fields.boolean('To Weigh With Scale', help="Check if the product should be weighted using the hardware scale integration"),
|
||||
'pos_categ_id': fields.many2one('pos.category','Point of Sale Category', help="Those categories are used to group similar products for point of sale."),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'to_weight' : False,
|
||||
'available_in_pos': True,
|
||||
}
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
product_ctx = dict(context or {}, active_test=False)
|
||||
if self.search_count(cr, uid, [('id', 'in', ids), ('available_in_pos', '=', True)], context=product_ctx):
|
||||
if self.pool['pos.session'].search_count(cr, uid, [('state', '!=', 'closed')], context=context):
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('You cannot delete a product saleable in point of sale while a session is still opened.'))
|
||||
return super(product_template, self).unlink(cr, uid, ids, context=context)
|
||||
@@ -6,4 +6,22 @@ class res_users(osv.osv):
|
||||
_inherit = 'res.partner'
|
||||
_columns = {
|
||||
'barcode' : fields.char('Barcode', help="BarCode", oldname='ean13'),
|
||||
}
|
||||
}
|
||||
|
||||
def create_from_ui(self, cr, uid, partner, context=None):
|
||||
""" create or modify a partner from the point of sale ui.
|
||||
partner contains the partner's fields. """
|
||||
|
||||
#image is a dataurl, get the data after the comma
|
||||
if partner.get('image',False):
|
||||
img = partner['image'].split(',')[1]
|
||||
partner['image'] = img
|
||||
|
||||
if partner.get('id',False): # Modifying existing partner
|
||||
partner_id = partner['id']
|
||||
del partner['id']
|
||||
self.write(cr, uid, [partner_id], partner, context=context)
|
||||
else:
|
||||
partner_id = self.create(cr, uid, partner, context=context)
|
||||
|
||||
return partner_id
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.actions.act_window" id="action_pos_invoice">
|
||||
<field name="name">Invoices</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('origin','like','POS')]</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="action_account_journal_form" model="ir.actions.act_window">
|
||||
<field name="name">Payment Methods</field>
|
||||
<field name="res_model">account.journal</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context" eval="{'default_journal_user': 1, 'default_type': 'cash'}"/>
|
||||
<field name="domain">[('journal_user','=', 1)]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a payment method.
|
||||
</p><p>
|
||||
Payment methods are defined by accounting journals having the
|
||||
field <i>PoS Payment Method</i> checked. In order to be useable
|
||||
from the touchscreen interface, you must set the payment method
|
||||
on the <i>Point of Sale</i> configuration.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_account_journal_form"
|
||||
id="menu_action_account_journal_form_open"
|
||||
parent="menu_point_config_product"
|
||||
sequence="20"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -56,5 +56,223 @@
|
||||
name="point_of_sale.report_sessionsummary"
|
||||
file="point_of_sale.report_sessionsummary"
|
||||
/>
|
||||
|
||||
<!-- report -->
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_trans_user_form">
|
||||
<field name="name">report.trans.pos.user.form</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS ">
|
||||
<group col="4">
|
||||
<field name="user_id"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="jl_id"/>
|
||||
<field name="date_create" widget="date"/>
|
||||
<field name="no_trans"/>
|
||||
<field name="amount"/>
|
||||
<field name="invoice_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_trans_pos_user_tree">
|
||||
<field name="name">Sales by user</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS">
|
||||
<field name="date_create" widget="date" />
|
||||
<field name="user_id"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="jl_id"/>
|
||||
<field name="no_trans" sum="Total Transaction"/>
|
||||
<field name="amount" sum="Amount total"/>
|
||||
<field name="product_nb" />
|
||||
<field name="invoice_id"/>
|
||||
<field name="disc"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_transaction_pos_calendar">
|
||||
<field name="name">report.transaction.pos.calendar</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="user_id" date_start="date_create" string="POS Report">
|
||||
<field name="journal_id"/>
|
||||
<field name="amount"/>
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_transaction_pos_graph">
|
||||
<field name="name">report.transaction.pos.graph</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<graph type="bar" string="POS Report">
|
||||
<field name="user_id" />
|
||||
<field name="amount"/>
|
||||
<field group="True" name="journal_id"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_trans_pos_tree_today">
|
||||
<field name="name">Sales by day</field>
|
||||
<field name="res_model">report.transaction.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_create','=',time.strftime('%Y-%m-%d'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_trans_pos_tree_month">
|
||||
<field name="name">Sales by month</field>
|
||||
<field name="res_model">report.transaction.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_create','like',time.strftime('%Y-%m'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_trans_pos_tree">
|
||||
<field name="name">Sales by user</field>
|
||||
<field name="res_model">report.transaction.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="view_id" ref="view_trans_pos_user_tree"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_form">
|
||||
<field name="name">report.sales.by.user.pos.form</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS ">
|
||||
<group col="4">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_tree">
|
||||
<field name="name">report.sales.by.user.pos.tree</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_calendar">
|
||||
<field name="name">report.sales.by.user.pos.calendar</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="user_id" date_start="date_order" string="POS Report">
|
||||
<field name="qty"/>
|
||||
<field name="amount" />
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_graph">
|
||||
<field name="name">report.sales.by.user.pos.graph</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<graph type="bar" string="Sales by User">
|
||||
<field name="user_id" />
|
||||
<field name="amount"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_report_sales_by_user_pos_today">
|
||||
<field name="name">Sales by User</field>
|
||||
<field name="res_model">report.sales.by.user.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_order','=',time.strftime('%Y-%m-%d'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Sales of user by month -->
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_form">
|
||||
<field name="name">report.sales.by.user.pos.month.form</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS ">
|
||||
<group col="4">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_tree">
|
||||
<field name="name">report.sales.by.user.pos.month.tree</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_calendar">
|
||||
<field name="name">report.sales.by.user.pos.month.calendar</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="user_id" date_start="date_order" string="POS Report">
|
||||
<field name="qty"/>
|
||||
<field name="amount" />
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_graph">
|
||||
<field name="name">report.sales.by.user.pos.month.graph</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<graph type="bar" string="Sales by User">
|
||||
<field name="user_id" />
|
||||
<field name="amount"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_report_sales_by_user_pos_month">
|
||||
<field name="name">Sales by User Monthly</field>
|
||||
<field name="res_model">report.sales.by.user.pos.month</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_order','like',time.strftime('%Y-%m'))]</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -10,989 +10,5 @@
|
||||
web_icon="point_of_sale,static/description/icon.png"
|
||||
sequence="20"/>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_pos_form">
|
||||
<field name="name">pos.order</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Point of Sale Orders">
|
||||
<header>
|
||||
<button name="%(action_pos_payment)d" string="Payment" class="oe_highlight" type="action" states="draft" context="{'pos_session_id' : session_id}"/>
|
||||
<button name="action_invoice" string="Invoice" type="object" states="paid" attrs="{'readonly': [('partner_id','=',False)]}"/>
|
||||
<button name="refund" string="Return Products" type="object"
|
||||
attrs="{'invisible':[('state','=','draft')]}"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,paid,done" statusbar_colors='{"cancel":"red"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group col="4" colspan="4" name="order_fields">
|
||||
<field name="name"/>
|
||||
<field name="date_order"/>
|
||||
<field name="session_id" />
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer', '=', True)]" context="{'search_default_customer':1}" attrs="{'readonly': [('state','=','invoiced')]}"/>
|
||||
<field name="fiscal_position_id" options="{'no_create': True}"/>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Products">
|
||||
<field name="lines" colspan="4" nolabel="1">
|
||||
<tree string="Order lines" editable="bottom">
|
||||
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,qty,parent.partner_id)"/>
|
||||
<field name="qty" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)"/>
|
||||
<field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="tax_ids_after_fiscal_position" widget="many2many_tags"/>
|
||||
<field name="price_subtotal" widget="monetary"/>
|
||||
<field name="price_subtotal_incl" widget="monetary"/>
|
||||
</tree>
|
||||
<form string="Order lines">
|
||||
<group col="4">
|
||||
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,qty,parent.partner_id)"/>
|
||||
<field name="qty" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)"/>
|
||||
<field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="price_subtotal" invisible="1" widget="monetary"/>
|
||||
<field name="price_subtotal_incl" invisible="1" widget="monetary"/>
|
||||
<field name="tax_ids" widget="many2many_tags"/>
|
||||
<field name="notice"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
<group class="oe_subtotal_footer oe_right" colspan="2" name="order_total">
|
||||
<field name="amount_tax" widget="monetary"/>
|
||||
<div class="oe_subtotal_footer_separator oe_inline">
|
||||
<label for="amount_total" />
|
||||
<button name="button_dummy"
|
||||
states="draft" string="(update)" class="oe_edit_only oe_link"/>
|
||||
</div>
|
||||
<field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator" widget="monetary"/>
|
||||
</group>
|
||||
<div class="oe_clear"/>
|
||||
</page>
|
||||
<page string="Payments">
|
||||
<field name="statement_ids" colspan="4" nolabel="1">
|
||||
<tree editable="bottom" string="Statement lines">
|
||||
<field name="journal_id"/>
|
||||
<field name="statement_id"/>
|
||||
<field name="amount"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page name="extra" string="Extra Info">
|
||||
<group string="General Information">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="location_id" widget="selection" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="user_id"/>
|
||||
<field name="pricelist_id" groups="product.group_sale_pricelist"/>
|
||||
<field name="picking_id" readonly="1"/>
|
||||
<field name="pos_reference"/>
|
||||
</group>
|
||||
<group string="Accounting Information">
|
||||
<group>
|
||||
<field name="sale_journal" domain="[('type','=','sale')]"/>
|
||||
<field name="invoice_id" readonly="1"
|
||||
attrs="{'invisible':[('state','<>','invoiced')]}"/>
|
||||
<field name="account_move" readonly="1"
|
||||
attrs="{'invisible':[('state','<>','done')]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<button name="%(pos_invoice_report)d" string="Re-Print"
|
||||
icon="gtk-print" type="action" attrs="{'invisible':[('state','<>','invoiced')]}"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Notes" >
|
||||
<field name="note"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_pos_pos_form">
|
||||
<field name="name">Orders</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="domain">[]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new order.
|
||||
</p><p>
|
||||
Use this menu to browse previous orders. To record new
|
||||
orders, you may use the menu <i>Your Session</i> for
|
||||
the touchscreen interface.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="view_pos_order_tree">
|
||||
<field name="name">Orders</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS Orders" decoration-info="state == 'draft'" decoration-muted="state in ('done','cancel')">
|
||||
<field name="name"/>
|
||||
<field name="pos_reference"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="date_order"/>
|
||||
<field name="user_id"/>
|
||||
<field name="invoice_id"/>
|
||||
<field name="amount_total" sum="Amount total" widget="monetary"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="state"/>
|
||||
<field name="session_id" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_pos_order_filter" model="ir.ui.view">
|
||||
<field name="name">pos.order.list.select</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Sales Order">
|
||||
<field name="name" string="Sales Order"/>
|
||||
<field name="date_order"/>
|
||||
<field name="pos_reference"/>
|
||||
<filter string="New" domain="[('state','=','draft')]"/>
|
||||
<filter string="Done" domain="[('state','in',('paid','invoiced','done'))]"/>
|
||||
<filter string="Invoiced" domain="[('state','=','invoiced')]"/>
|
||||
<filter string="Posted" domain="[('state','=','done')]"/>
|
||||
<field name="user_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Customer" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Order Month" domain="[]" context="{'group_by':'date_order'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_search">
|
||||
<field name="name">pos.order.search.view</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Point of Sale Orders">
|
||||
<field name="name"/>
|
||||
<field name="config_id"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Orders" id="menu_point_of_sale" parent="menu_point_root" sequence="10"/>
|
||||
<menuitem parent="menu_point_of_sale" id="menu_point_ofsale" action="action_pos_pos_form" sequence="1" groups="group_pos_manager,group_pos_user"/>
|
||||
|
||||
<record id="product_template_action" model="ir.actions.act_window">
|
||||
<field name="name">Products</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">product.template</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,kanban</field>
|
||||
<field name="context" eval="{}"/>
|
||||
<field name="domain" eval="[('available_in_pos','<>',False)]"/>
|
||||
<field name="search_view_id" eval="False"/> <!-- Force empty -->
|
||||
<field name="view_id" eval="False"/> <!-- Force empty -->
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a new product.
|
||||
</p><p>
|
||||
You must define a product for everything you sell through
|
||||
the point of sale interface.
|
||||
</p><p>
|
||||
Do not forget to set the price and the point of sale category
|
||||
in which it should appear. If a product has no point of sale
|
||||
category, you can not sell it through the point of sale
|
||||
interface.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem
|
||||
action="product_template_action"
|
||||
id="menu_pos_products"
|
||||
parent="menu_point_of_sale" sequence="3"/>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_line">
|
||||
<field name="name">Sale lines</field>
|
||||
<field name="model">pos.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS Order lines">
|
||||
<field name="product_id" readonly="1"/>
|
||||
<field name="qty" readonly="1" sum="Total qty"/>
|
||||
<field name="discount" readonly="1" widget="monetary"/>
|
||||
<field name="price_unit" readonly="1" widget="monetary"/>
|
||||
<field name="price_subtotal" readonly="1" sum="Sum of subtotals" widget="monetary"/>
|
||||
<field name="price_subtotal_incl" readonly="1" sum="Sum of subtotals" widget="monetary"/>
|
||||
<field name="create_date" readonly="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_line_form">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="model">pos.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS Order line">
|
||||
<group col="4">
|
||||
<field name="product_id" />
|
||||
<field name="qty" />
|
||||
<field name="discount" widget="monetary"/>
|
||||
<field name="price_unit" widget="monetary"/>
|
||||
<field name="create_date" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_order_line">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_pos_order_line"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_order_line_form">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form,tree</field>
|
||||
<field name="view_id" ref="view_pos_order_line_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_order_line_day">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_pos_order_line"/>
|
||||
<field name="domain">[('create_date', '>=', time.strftime('%Y-%m-%d 00:00:00')),('create_date', '<=', time.strftime('%Y-%m-%d 23:59:59'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<!-- report -->
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_trans_user_form">
|
||||
<field name="name">report.trans.pos.user.form</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS ">
|
||||
<group col="4">
|
||||
<field name="user_id"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="jl_id"/>
|
||||
<field name="date_create" widget="date"/>
|
||||
<field name="no_trans"/>
|
||||
<field name="amount"/>
|
||||
<field name="invoice_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_trans_pos_user_tree">
|
||||
<field name="name">Sales by user</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS">
|
||||
<field name="date_create" widget="date" />
|
||||
<field name="user_id"/>
|
||||
<field name="journal_id"/>
|
||||
<field name="jl_id"/>
|
||||
<field name="no_trans" sum="Total Transaction"/>
|
||||
<field name="amount" sum="Amount total"/>
|
||||
<field name="product_nb" />
|
||||
<field name="invoice_id"/>
|
||||
<field name="disc"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_transaction_pos_calendar">
|
||||
<field name="name">report.transaction.pos.calendar</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="user_id" date_start="date_create" string="POS Report">
|
||||
<field name="journal_id"/>
|
||||
<field name="amount"/>
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_transaction_pos_graph">
|
||||
<field name="name">report.transaction.pos.graph</field>
|
||||
<field name="model">report.transaction.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<graph type="bar" string="POS Report">
|
||||
<field name="user_id" />
|
||||
<field name="amount"/>
|
||||
<field group="True" name="journal_id"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_trans_pos_tree_today">
|
||||
<field name="name">Sales by day</field>
|
||||
<field name="res_model">report.transaction.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_create','=',time.strftime('%Y-%m-%d'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_trans_pos_tree_month">
|
||||
<field name="name">Sales by month</field>
|
||||
<field name="res_model">report.transaction.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_create','like',time.strftime('%Y-%m'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_trans_pos_tree">
|
||||
<field name="name">Sales by user</field>
|
||||
<field name="res_model">report.transaction.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="view_id" ref="view_trans_pos_user_tree"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_form">
|
||||
<field name="name">report.sales.by.user.pos.form</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS ">
|
||||
<group col="4">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_tree">
|
||||
<field name="name">report.sales.by.user.pos.tree</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_calendar">
|
||||
<field name="name">report.sales.by.user.pos.calendar</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="user_id" date_start="date_order" string="POS Report">
|
||||
<field name="qty"/>
|
||||
<field name="amount" />
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_graph">
|
||||
<field name="name">report.sales.by.user.pos.graph</field>
|
||||
<field name="model">report.sales.by.user.pos</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<graph type="bar" string="Sales by User">
|
||||
<field name="user_id" />
|
||||
<field name="amount"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_report_sales_by_user_pos_today">
|
||||
<field name="name">Sales by User</field>
|
||||
<field name="res_model">report.sales.by.user.pos</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_order','=',time.strftime('%Y-%m-%d'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Sales of user by month -->
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_form">
|
||||
<field name="name">report.sales.by.user.pos.month.form</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS ">
|
||||
<group col="4">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_tree">
|
||||
<field name="name">report.sales.by.user.pos.month.tree</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS">
|
||||
<field name="date_order" widget="date"/>
|
||||
<field name="qty"/>
|
||||
<field name="amount"/>
|
||||
<field name="user_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_calendar">
|
||||
<field name="name">report.sales.by.user.pos.month.calendar</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar color="user_id" date_start="date_order" string="POS Report">
|
||||
<field name="qty"/>
|
||||
<field name="amount" />
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_report_sales_by_user_pos_month_graph">
|
||||
<field name="name">report.sales.by.user.pos.month.graph</field>
|
||||
<field name="model">report.sales.by.user.pos.month</field>
|
||||
<field eval="2" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<graph type="bar" string="Sales by User">
|
||||
<field name="user_id" />
|
||||
<field name="amount"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_report_sales_by_user_pos_month">
|
||||
<field name="name">Sales by User Monthly</field>
|
||||
<field name="res_model">report.sales.by.user.pos.month</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,calendar,form,graph</field>
|
||||
<field name="domain">[('date_order','like',time.strftime('%Y-%m'))]</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<!-- Product Public Categories -->
|
||||
<record id="product_pos_category_form_view" model="ir.ui.view">
|
||||
<field name="name">pos.category.form</field>
|
||||
<field name="model">pos.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Pos Categories">
|
||||
<sheet>
|
||||
<field name="image_medium" widget='image' class="oe_avatar oe_right"/>
|
||||
<div class="oe_left">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="sequence"/>
|
||||
</group>
|
||||
</div>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="product_pos_category_tree_view" model="ir.ui.view">
|
||||
<field name="name">pos.category.tree</field>
|
||||
<field name="model">pos.category</field>
|
||||
<field name="field_parent" eval="False"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Product Product Categories">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="complete_name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="product_pos_category_action" model="ir.actions.act_window">
|
||||
<field name="name">Pos Product Categories</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.category</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to define a new category.
|
||||
</p><p>
|
||||
Categories are used to browse your products through the
|
||||
touchscreen interface.
|
||||
</p><p>
|
||||
If you put a photo on the category and enable the option in the
|
||||
point of sale configuration, the layout of the touchscreen interface
|
||||
will automatically be displayed. We suggest not to put a photo
|
||||
on categories for small (1024x768) screens.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem name="Configuration" parent="menu_point_root"
|
||||
id="menu_point_config_product" sequence="100" groups="group_pos_manager,base.group_configuration"/>
|
||||
<menuitem action="product_pos_category_action" id="menu_product_pos_category" parent="menu_point_config_product" sequence="20" />
|
||||
<!-- END -->
|
||||
|
||||
<record id="product_template_form_view" model="ir.ui.view">
|
||||
<field name="name">product.template.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="sale_condition" position="after">
|
||||
<group name="pos" string="Point of Sale">
|
||||
<field name="available_in_pos"/>
|
||||
<field name="pos_categ_id" attrs="{'invisible': [('available_in_pos', '=', False)]}"/>
|
||||
<field name="to_weight" attrs="{'invisible': [('available_in_pos', '=', False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
<record id="product_template_tree_view" model="ir.ui.view">
|
||||
<field name="name">product.template.tree.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_tree_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="before">
|
||||
<field name="sequence" widget="handle" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- END -->
|
||||
|
||||
<record id="action_account_journal_form" model="ir.actions.act_window">
|
||||
<field name="name">Payment Methods</field>
|
||||
<field name="res_model">account.journal</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context" eval="{'default_journal_user': 1, 'default_type': 'cash'}"/>
|
||||
<field name="domain">[('journal_user','=', 1)]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a payment method.
|
||||
</p><p>
|
||||
Payment methods are defined by accounting journals having the
|
||||
field <i>PoS Payment Method</i> checked. In order to be useable
|
||||
from the touchscreen interface, you must set the payment method
|
||||
on the <i>Point of Sale</i> configuration.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_account_journal_form"
|
||||
id="menu_action_account_journal_form_open"
|
||||
parent="menu_point_config_product"
|
||||
sequence="20"/>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_tree_all_sales_lines">
|
||||
<field name="name">POS Sales Lines</field>
|
||||
<field name="model">pos.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS Orders lines">
|
||||
<field name="order_id" />
|
||||
<field name="create_date" />
|
||||
<field name="product_id" />
|
||||
<field name="qty" />
|
||||
<field name="price_unit" widget="monetary"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_pos_all_sales_lines">
|
||||
<field name="name">All sales lines</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_pos_order_tree_all_sales_lines" />
|
||||
</record>
|
||||
|
||||
<!-- Miscellaneous Operations/Reporting -->
|
||||
<menuitem name="Reports" parent="menu_point_root" id="menu_point_rep" sequence="99" groups="group_pos_manager"/>
|
||||
<!-- Invoice -->
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_invoice">
|
||||
<field name="name">Invoices</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('origin','like','POS')]</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_report_pos_details"
|
||||
id="menu_pos_details" parent="menu_point_rep" sequence="6" />
|
||||
|
||||
<record model="ir.actions.act_url" id="action_pos_pos">
|
||||
<field name="name">Start Point of Sale</field>
|
||||
<field name="url">/pos/web/</field>
|
||||
<field name="target">self</field>
|
||||
</record>
|
||||
|
||||
<!-- Top menu item -->
|
||||
<record model="ir.ui.view" id="view_pos_config_form">
|
||||
<field name="name">pos.config.form.view</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Point of Sale Configuration">
|
||||
<header>
|
||||
<button string="Set to Active" name="set_active" type="object" states="inactive,deprecated"/>
|
||||
<button string="Set to Inactive" name="set_inactive" type="object" states="active" />
|
||||
<button string="Set to Deprecated" name="set_deprecate" type="object" states="active,inactive" />
|
||||
<field name="state" widget="statusbar" statusbar_visible="active,inactive,deprecated" statusbar_colors='{"deprecated" : "red"}' nolabel="1"/>
|
||||
</header>
|
||||
|
||||
<sheet>
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
<group col="4">
|
||||
<field name="picking_type_id" widget="selection" groups="stock.group_stock_multi_locations"
|
||||
on_change="onchange_picking_type_id(picking_type_id)"/>
|
||||
<field name="stock_location_id" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="pricelist_id" groups="product.group_sale_pricelist"/>
|
||||
<field name="journal_id" widget="selection"/>
|
||||
<field name="group_by" groups="account.group_account_user"/>
|
||||
<field name="barcode_nomenclature_id" />
|
||||
<field name="sequence_id" readonly="1" groups="base.group_no_one"/>
|
||||
<field name="fiscal_position_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
<separator string="Available Payment Methods" colspan="4"/>
|
||||
<field name="journal_ids" colspan="4" nolabel="1" context="{'default_journal_user': 1}">
|
||||
<tree string="Journals">
|
||||
<field name="code" />
|
||||
<field name="name" />
|
||||
<field name="type" />
|
||||
</tree>
|
||||
</field>
|
||||
<group name="features" string="Features" >
|
||||
<group>
|
||||
<field name="iface_vkeyboard" />
|
||||
<field name="iface_invoicing" />
|
||||
<field name="iface_precompute_cash" />
|
||||
<field name="iface_start_categ_id" />
|
||||
<field name="tip_product_id" />
|
||||
<field name="iface_tax_included" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="iface_big_scrollbars" />
|
||||
<field name="iface_print_auto" />
|
||||
<field name="iface_display_categ_images" />
|
||||
<field name="iface_print_skip_screen" attrs="{'invisible' : [('iface_print_auto', '=', False)]}" />
|
||||
<field name="cash_control" />
|
||||
</group>
|
||||
</group>
|
||||
<group string="Hardware Proxy / PosBox" >
|
||||
<field name="proxy_ip" />
|
||||
<field name="iface_print_via_proxy" string="Receipt Printer" on_change="onchange_iface_print_via_proxy(iface_print_via_proxy)"/>
|
||||
<field name="iface_scan_via_proxy" string="Barcode Scanner"/>
|
||||
<field name="iface_electronic_scale" />
|
||||
<field name="iface_cashdrawer" />
|
||||
</group>
|
||||
<group name="receipt" string="Receipt" >
|
||||
<field name="receipt_header" string="Header" placeholder="A custom receipt header message"/>
|
||||
<field name="receipt_footer" string="Footer" placeholder="A custom receipt footer message"/>
|
||||
</group>
|
||||
</sheet>
|
||||
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_config_tree">
|
||||
<field name="name">pos.config.tree.view</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Point of Sale Configuration" decoration-muted="state == 'inactive'">
|
||||
<field name="name" />
|
||||
<field name="stock_location_id" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_config_search">
|
||||
<field name="name">pos.config.search.view</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Point of Sale Config">
|
||||
<field name="name" />
|
||||
<filter string="Active" domain="[('state', '=', 'active')]" />
|
||||
<filter string="Inactive" domain="[('state', '=', 'inactive')]" />
|
||||
<field name="stock_location_id" groups="stock.group_stock_multi_locations" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Sessions -->
|
||||
|
||||
<act_window
|
||||
id="act_pos_config_sessions"
|
||||
name="Sessions"
|
||||
src_model="pos.config"
|
||||
res_model="pos.session"
|
||||
domain="[('config_id', '=', active_id)]" />
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_config_pos">
|
||||
<field name="name">Point of Sale</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.config</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_pos_config_search" />
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_point_config_product"
|
||||
action="action_pos_config_pos"
|
||||
id="menu_pos_config_pos"
|
||||
groups="group_pos_manager"/>
|
||||
|
||||
<act_window
|
||||
id="act_pos_session_orders"
|
||||
name="Orders"
|
||||
src_model="pos.session"
|
||||
res_model="pos.order"
|
||||
context="{'search_default_session_id': active_id, 'default_session_id' : active_id }" />
|
||||
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_session_form">
|
||||
<field name="name">pos.session.form.view</field>
|
||||
<field name="model">pos.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Point of Sale Session">
|
||||
<header>
|
||||
<button name="open" type="workflow" string="Open Session"
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', False), ('state', '!=', 'opening_control')]}"
|
||||
class="oe_highlight"/>
|
||||
<button name="open_frontend_cb" type="object" string="Continue Selling" states="opened"
|
||||
class="oe_highlight"/>
|
||||
<button name="cashbox_control" type="workflow" string="End of Session"
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', '!=', 'opened')]}"
|
||||
class="oe_highlight" />
|
||||
<button name="close" type="workflow" string="Validate Closing & Post Entries"
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', True),('state', '!=', 'opened')]}"
|
||||
class="oe_highlight" />
|
||||
<button name="close" type="workflow" string="Validate Closing & Post Entries" states="closing_control"
|
||||
class="oe_highlight" />
|
||||
|
||||
<field name="state" widget="statusbar" statusbar_visible="opening_control,opened,closing_control,closed" nolabel="1" />
|
||||
|
||||
</header>
|
||||
<sheet>
|
||||
<field name="cash_register_id" invisible="1"/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" name="%(action_pos_box_in)d"
|
||||
type="action" icon="fa-level-down"
|
||||
attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', 'not in', ['opened', 'closing_control'])]}">
|
||||
<div class="o_form_field o_stat_info">
|
||||
<span class="o_stat_text">Put</span>
|
||||
<span class="o_stat_text">Money In</span>
|
||||
</div>
|
||||
</button>
|
||||
<button class="oe_stat_button" name="%(action_pos_box_out)d"
|
||||
type="action" icon="fa-level-up"
|
||||
attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', 'not in', ['opened', 'closing_control'])]}">
|
||||
<div class="o_form_field o_stat_info">
|
||||
<span class="o_stat_text">Take</span>
|
||||
<span class="o_stat_text">Money Out</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="%(account.action_view_account_bnk_stmt_cashbox)d"
|
||||
class="oe_stat_button"
|
||||
attrs="{'invisible':['|', ('cash_control', '=', False), ('state', '!=', 'opening_control')]}"
|
||||
icon="fa-money"
|
||||
type="action" context="{'balance': 'start', 'bank_statement_id': cash_register_id}">
|
||||
<span class="o_stat_text">Set Opening Balance</span>
|
||||
</button>
|
||||
<button name="%(account.action_view_account_bnk_stmt_cashbox)d"
|
||||
class="oe_stat_button"
|
||||
attrs="{'invisible':['|', '|', ('cash_control', '=', False), ('state', '=', 'opening_control'), ('state', '=', 'closed')]}"
|
||||
icon="fa-money"
|
||||
type="action" context="{'balance': 'end', 'bank_statement_id': cash_register_id}">
|
||||
<span class="o_stat_text">Set Closing Balance</span>
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="oe_title">
|
||||
Session:
|
||||
<field name="name" attrs="{'invisible': [('name','=','/')]}" class="oe_inline"/>
|
||||
</h1>
|
||||
<group>
|
||||
<field name="cash_control" invisible="1" />
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="config_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="start_at" attrs="{'invisible' : [('state', '=', 'opening_control')]}"/>
|
||||
<field name="stop_at" attrs="{'invisible' : [('state', '!=', 'closed')]}"/>
|
||||
</group>
|
||||
<newline/>
|
||||
|
||||
<div attrs="{'invisible' : [('cash_control', '=', False)]}">
|
||||
<group class="oe_subtotal_footer oe_right">
|
||||
<field name="cash_register_balance_start" readonly="1" string="Opening Balance" class="oe_subtotal_footer_separator" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<label for="cash_register_total_entry_encoding" attrs="{'invisible' : [('state', '=', 'opening_control')]}" string="+ Transactions" class="oe_force_bold oe_opening_total"/>
|
||||
<field name="cash_register_total_entry_encoding" nolabel="1" attrs="{'invisible' : [('state', '=', 'opening_control')]}" class="oe_bold oe_pos_total" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<label for="cash_register_balance_end" attrs="{'invisible' : [('state', '=', 'opening_control')]}" string="= Theoretical Closing Balance" class="oe_force_bold oe_opening_total"/>
|
||||
<field name="cash_register_balance_end" nolabel="1" attrs="{'invisible' : [('state', '=', 'opening_control')]}" class="oe_bold oe_pos_total" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
</div>
|
||||
<div>
|
||||
<group class="oe_subtotal_footer oe_right" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', '=', 'opening_control')]}">
|
||||
<field name="cash_register_balance_end_real" string="Real Closing Balance" class="oe_subtotal_footer_separator" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
<group/>
|
||||
<group/>
|
||||
<group class="oe_subtotal_footer oe_right" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', '=', 'opening_control')]}">
|
||||
<field name="cash_register_difference" class="oe_subtotal_footer_separator oe_right oe_pos_total oe_pos_difference" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
</div>
|
||||
</group>
|
||||
<separator string="Summary by Payment Methods" attrs="{'invisible' : [('state', '=', 'opening_control')]}"/>
|
||||
<field name="statement_ids" attrs="{'invisible' : [('state', '=', 'opening_control')]}">
|
||||
<tree string="Statements">
|
||||
<field name="name" />
|
||||
<field name="journal_id" />
|
||||
<field name="balance_start" />
|
||||
<field name="total_entry_encoding" />
|
||||
<field name="balance_end_real" />
|
||||
<field name="difference" />
|
||||
<field name="currency_id" groups="base.group_multi_currency" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_session_tree">
|
||||
<field name="name">pos.session.tree.view</field>
|
||||
<field name="model">pos.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Point of Sale Session">
|
||||
<field name="config_id" />
|
||||
<field name="name" />
|
||||
<field name="user_id" />
|
||||
<field name="start_at" />
|
||||
<field name="stop_at" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_session_search">
|
||||
<field name="name">pos.session.search.view</field>
|
||||
<field name="model">pos.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Point of Sale Session">
|
||||
<field name="name" />
|
||||
<filter string="Open" domain="[('state', '=', 'opened')]" />
|
||||
<separator/>
|
||||
<filter string="Today" domain="[('start_at', '>=', datetime.datetime.combine(context_today(), datetime.time(0,0,0)))]" />
|
||||
<field name="config_id" />
|
||||
<field name="user_id" />
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="User" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
<filter string="Point of Sale" domain="[]" context="{'group_by': 'user_id'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_session">
|
||||
<field name="name">Sessions</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.session</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_pos_session_search" />
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to start a new session.
|
||||
</p><p>
|
||||
A session is a period of time, usually one day, during which
|
||||
you sell through the point of sale. The user has to check the
|
||||
currencies in your cash registers at the beginning and the end
|
||||
of each session.
|
||||
</p><p>
|
||||
Note that you may use the menu <i>Your Session</i>
|
||||
to quickly open a new session.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_point_of_sale"
|
||||
action="action_pos_session"
|
||||
id="menu_pos_session_all"
|
||||
sequence="2"
|
||||
groups="group_pos_manager"/>
|
||||
|
||||
<record id="view_pos_order_filter" model="ir.ui.view">
|
||||
<field name="name">pos.order.list.select</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Sales Order">
|
||||
<field name="name"/>
|
||||
<field name="date_order"/>
|
||||
<filter string="New" domain="[('state','=','draft')]"/>
|
||||
<filter string="Done" domain="[('state','in',('paid','invoiced','done'))]"/>
|
||||
<filter string="Invoiced" domain="[('state','=','invoiced')]"/>
|
||||
<filter string="Posted" domain="[('state','=','done')]"/>
|
||||
<field name="user_id"/>
|
||||
<field name="session_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Customer" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Salesman" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Session" domain="[]" context="{'group_by':'session_id'}"/>
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Order Month" domain="[]" context="{'group_by':'date_order'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_config_kanban">
|
||||
<field name="name">Point of Sale</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.config</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="search_view_id" ref="view_pos_config_search" />
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new PoS config.
|
||||
</p><p>
|
||||
Configure at least one Point of Sale to be able to sell through the PoS interface.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_pos_config_kanban"
|
||||
parent="menu_point_root" name="Dashboard"
|
||||
id="menu_pos_dashboard" sequence="1"/>
|
||||
|
||||
<!-- Barcode Nomenclatures -->
|
||||
<menuitem parent="menu_point_config_product" action="barcodes.action_barcode_nomenclature_form" id="menu_pos_barcode_nomenclature_all"
|
||||
sequence="30"/>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="product_pos_category_form_view" model="ir.ui.view">
|
||||
<field name="name">pos.category.form</field>
|
||||
<field name="model">pos.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Pos Categories">
|
||||
<sheet>
|
||||
<field name="image_medium" widget='image' class="oe_avatar oe_right"/>
|
||||
<div class="oe_left">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="sequence"/>
|
||||
</group>
|
||||
</div>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="product_pos_category_tree_view" model="ir.ui.view">
|
||||
<field name="name">pos.category.tree</field>
|
||||
<field name="model">pos.category</field>
|
||||
<field name="field_parent" eval="False"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Product Product Categories">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="complete_name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="product_pos_category_action" model="ir.actions.act_window">
|
||||
<field name="name">Pos Product Categories</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.category</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to define a new category.
|
||||
</p><p>
|
||||
Categories are used to browse your products through the
|
||||
touchscreen interface.
|
||||
</p><p>
|
||||
If you put a photo on the category and enable the option in the
|
||||
point of sale configuration, the layout of the touchscreen interface
|
||||
will automatically be displayed. We suggest not to put a photo
|
||||
on categories for small (1024x768) screens.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="product_pos_category_action" id="menu_product_pos_category" parent="menu_point_config_product" sequence="20" />
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="view_pos_config_form">
|
||||
<field name="name">pos.config.form.view</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Point of Sale Configuration">
|
||||
<header>
|
||||
<button string="Set to Active" name="set_active" type="object" states="inactive,deprecated"/>
|
||||
<button string="Set to Inactive" name="set_inactive" type="object" states="active" />
|
||||
<button string="Set to Deprecated" name="set_deprecate" type="object" states="active,inactive" />
|
||||
<field name="state" widget="statusbar" statusbar_visible="active,inactive,deprecated" statusbar_colors='{"deprecated" : "red"}' nolabel="1"/>
|
||||
</header>
|
||||
|
||||
<sheet>
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
<group col="4">
|
||||
<field name="picking_type_id" widget="selection" groups="stock.group_stock_multi_locations"
|
||||
on_change="onchange_picking_type_id(picking_type_id)"/>
|
||||
<field name="stock_location_id" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="pricelist_id" groups="product.group_sale_pricelist"/>
|
||||
<field name="journal_id" widget="selection"/>
|
||||
<field name="group_by" groups="account.group_account_user"/>
|
||||
<field name="barcode_nomenclature_id" />
|
||||
<field name="sequence_id" readonly="1" groups="base.group_no_one"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
<separator string="Available Payment Methods" colspan="4"/>
|
||||
<field name="journal_ids" colspan="4" nolabel="1" context="{'default_journal_user': 1}">
|
||||
<tree string="Journals">
|
||||
<field name="code" />
|
||||
<field name="name" />
|
||||
<field name="type" />
|
||||
</tree>
|
||||
</field>
|
||||
<group name="features" string="Features" >
|
||||
<group>
|
||||
<field name="iface_vkeyboard" />
|
||||
<field name="iface_invoicing" />
|
||||
<field name="iface_precompute_cash" />
|
||||
<field name="iface_start_categ_id" />
|
||||
<field name="tip_product_id" />
|
||||
<field name="iface_tax_included" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="iface_big_scrollbars" />
|
||||
<field name="iface_print_auto" />
|
||||
<field name="iface_display_categ_images" />
|
||||
<field name="iface_print_skip_screen" attrs="{'invisible' : [('iface_print_auto', '=', False)]}" />
|
||||
</group>
|
||||
</group>
|
||||
<group string="Hardware Proxy / PosBox" >
|
||||
<field name="proxy_ip" />
|
||||
<field name="iface_print_via_proxy" string="Receipt Printer"/>
|
||||
<field name="iface_scan_via_proxy" string="Barcode Scanner"/>
|
||||
<field name="iface_electronic_scale" />
|
||||
<field name="iface_cashdrawer" />
|
||||
</group>
|
||||
<group name="receipt" string="Receipt" >
|
||||
<field name="receipt_header" string="Header" placeholder="A custom receipt header message"/>
|
||||
<field name="receipt_footer" string="Footer" placeholder="A custom receipt footer message"/>
|
||||
</group>
|
||||
</sheet>
|
||||
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_config_tree">
|
||||
<field name="name">pos.config.tree.view</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Point of Sale Configuration" decoration-muted="state == 'inactive'">
|
||||
<field name="name" />
|
||||
<field name="stock_location_id" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_config_search">
|
||||
<field name="name">pos.config.search.view</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Point of Sale Config">
|
||||
<field name="name" />
|
||||
<filter string="Active" domain="[('state', '=', 'active')]" />
|
||||
<filter string="Inactive" domain="[('state', '=', 'inactive')]" />
|
||||
<field name="stock_location_id" groups="stock.group_stock_multi_locations" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
id="act_pos_config_sessions"
|
||||
name="Sessions"
|
||||
src_model="pos.config"
|
||||
res_model="pos.session"
|
||||
domain="[('config_id', '=', active_id)]" />
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_config_pos">
|
||||
<field name="name">Point of Sale</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.config</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_pos_config_search" />
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_point_config_product"
|
||||
action="action_pos_config_pos"
|
||||
id="menu_pos_config_pos"
|
||||
groups="group_pos_manager"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_config_kanban">
|
||||
<field name="name">Point of Sale</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.config</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="search_view_id" ref="view_pos_config_search" />
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new PoS config.
|
||||
</p><p>
|
||||
Configure at least one Point of Sale to be able to sell through the PoS interface.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Barcode Nomenclatures -->
|
||||
<menuitem parent="menu_point_config_product" action="barcodes.action_barcode_nomenclature_form" id="menu_pos_barcode_nomenclature_all"
|
||||
sequence="30"/>
|
||||
|
||||
<menuitem action="action_pos_config_kanban"
|
||||
parent="menu_point_root" name="Dashboard"
|
||||
id="menu_pos_dashboard" sequence="1"/>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,279 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="view_pos_pos_form">
|
||||
<field name="name">pos.order</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Point of Sale Orders">
|
||||
<header>
|
||||
<button name="%(action_pos_payment)d" string="Payment" class="oe_highlight" type="action" states="draft" context="{'pos_session_id' : session_id}"/>
|
||||
<button name="action_invoice" string="Invoice" type="object" states="paid" attrs="{'readonly': [('partner_id','=',False)]}"/>
|
||||
<button name="refund" string="Return Products" type="object"
|
||||
attrs="{'invisible':[('state','=','draft')]}"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,paid,done" statusbar_colors='{"cancel":"red"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group col="4" colspan="4" name="order_fields">
|
||||
<field name="name"/>
|
||||
<field name="date_order"/>
|
||||
<field name="session_id" />
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer', '=', True)]" context="{'search_default_customer':1}" attrs="{'readonly': [('state','=','invoiced')]}"/>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Products">
|
||||
<field name="lines" colspan="4" nolabel="1">
|
||||
<tree string="Order lines" editable="bottom">
|
||||
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,qty,parent.partner_id)"/>
|
||||
<field name="qty" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)"/>
|
||||
<field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="tax_ids" widget="many2many_tags"/>
|
||||
<field name="price_subtotal" widget="monetary"/>
|
||||
<field name="price_subtotal_incl" widget="monetary"/>
|
||||
</tree>
|
||||
<form string="Order lines">
|
||||
<group col="4">
|
||||
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,qty,parent.partner_id)"/>
|
||||
<field name="qty" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)"/>
|
||||
<field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
|
||||
<field name="price_subtotal" invisible="1" widget="monetary"/>
|
||||
<field name="price_subtotal_incl" invisible="1" widget="monetary"/>
|
||||
<field name="notice"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
<group class="oe_subtotal_footer oe_right" colspan="2" name="order_total">
|
||||
<field name="amount_tax" widget="monetary"/>
|
||||
<div class="oe_subtotal_footer_separator oe_inline">
|
||||
<label for="amount_total" />
|
||||
<button name="button_dummy"
|
||||
states="draft" string="(update)" class="oe_edit_only oe_link"/>
|
||||
</div>
|
||||
<field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator" widget="monetary"/>
|
||||
</group>
|
||||
<div class="oe_clear"/>
|
||||
</page>
|
||||
<page string="Payments">
|
||||
<field name="statement_ids" colspan="4" nolabel="1">
|
||||
<tree editable="bottom" string="Statement lines">
|
||||
<field name="journal_id"/>
|
||||
<field name="statement_id"/>
|
||||
<field name="amount"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page name="extra" string="Extra Info">
|
||||
<group string="General Information">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="location_id" widget="selection" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="user_id"/>
|
||||
<field name="pricelist_id" groups="product.group_sale_pricelist" domain="[('type','=','sale')]"/>
|
||||
<field name="picking_id" readonly="1"/>
|
||||
<field name="pos_reference"/>
|
||||
</group>
|
||||
<group string="Accounting Information">
|
||||
<group>
|
||||
<field name="sale_journal" domain="[('type','=','sale')]"/>
|
||||
<field name="invoice_id" readonly="1" attrs="{'invisible':[('state','<>','invoiced')]}"/>
|
||||
<field name="account_move" readonly="1" attrs="{'invisible':[('state','<>','done')]}"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Notes" >
|
||||
<field name="note"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_pos_pos_form">
|
||||
<field name="name">Orders</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="domain">[]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new order.
|
||||
</p><p>
|
||||
Use this menu to browse previous orders. To record new
|
||||
orders, you may use the menu <i>Your Session</i> for
|
||||
the touchscreen interface.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="view_pos_order_tree">
|
||||
<field name="name">Orders</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS Orders" decoration-info="state == 'draft'" decoration-muted="state in ('done','cancel')">
|
||||
<field name="name"/>
|
||||
<field name="pos_reference"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="date_order"/>
|
||||
<field name="user_id"/>
|
||||
<field name="invoice_id"/>
|
||||
<field name="amount_total" sum="Amount total" widget="monetary"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="state"/>
|
||||
<field name="session_id" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_pos_order_filter" model="ir.ui.view">
|
||||
<field name="name">pos.order.list.select</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Sales Order">
|
||||
<field name="name" string="Sales Order"/>
|
||||
<field name="date_order"/>
|
||||
<field name="pos_reference"/>
|
||||
<filter string="New" domain="[('state','=','draft')]"/>
|
||||
<filter string="Done" domain="[('state','in',('paid','invoiced','done'))]"/>
|
||||
<filter string="Invoiced" domain="[('state','=','invoiced')]"/>
|
||||
<filter string="Posted" domain="[('state','=','done')]"/>
|
||||
<field name="user_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Customer" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Order Month" domain="[]" context="{'group_by':'date_order'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_search">
|
||||
<field name="name">pos.order.search.view</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Point of Sale Orders">
|
||||
<field name="name"/>
|
||||
<field name="config_id"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem parent="menu_point_of_sale" id="menu_point_ofsale" action="action_pos_pos_form" sequence="1" groups="group_pos_manager,group_pos_user"/>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_line">
|
||||
<field name="name">Sale lines</field>
|
||||
<field name="model">pos.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS Order lines">
|
||||
<field name="product_id" readonly="1"/>
|
||||
<field name="qty" readonly="1" sum="Total qty"/>
|
||||
<field name="discount" readonly="1" widget="monetary"/>
|
||||
<field name="price_unit" readonly="1" widget="monetary"/>
|
||||
<field name="price_subtotal" readonly="1" sum="Sum of subtotals" widget="monetary"/>
|
||||
<field name="price_subtotal_incl" readonly="1" sum="Sum of subtotals" widget="monetary"/>
|
||||
<field name="create_date" readonly="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_line_form">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="model">pos.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS Order line">
|
||||
<group col="4">
|
||||
<field name="product_id" />
|
||||
<field name="qty" />
|
||||
<field name="discount" widget="monetary"/>
|
||||
<field name="price_unit" widget="monetary"/>
|
||||
<field name="create_date" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_order_line">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_pos_order_line"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_order_line_form">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form,tree</field>
|
||||
<field name="view_id" ref="view_pos_order_line_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_order_line_day">
|
||||
<field name="name">Sale line</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_pos_order_line"/>
|
||||
<field name="domain">[('create_date', '>=', time.strftime('%Y-%m-%d 00:00:00')),('create_date', '<=', time.strftime('%Y-%m-%d 23:59:59'))]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_order_tree_all_sales_lines">
|
||||
<field name="name">POS Sales Lines</field>
|
||||
<field name="model">pos.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="POS Orders lines">
|
||||
<field name="order_id" />
|
||||
<field name="create_date" />
|
||||
<field name="product_id" />
|
||||
<field name="qty" />
|
||||
<field name="price_unit" widget="monetary"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_pos_all_sales_lines">
|
||||
<field name="name">All sales lines</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_pos_order_tree_all_sales_lines" />
|
||||
</record>
|
||||
|
||||
<menuitem action="action_report_pos_details"
|
||||
id="menu_pos_details" parent="menu_point_rep" sequence="6" />
|
||||
|
||||
<record id="view_pos_order_filter" model="ir.ui.view">
|
||||
<field name="name">pos.order.list.select</field>
|
||||
<field name="model">pos.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Sales Order">
|
||||
<field name="name"/>
|
||||
<field name="date_order"/>
|
||||
<filter string="New" domain="[('state','=','draft')]"/>
|
||||
<filter string="Done" domain="[('state','in',('paid','invoiced','done'))]"/>
|
||||
<filter string="Invoiced" domain="[('state','=','invoiced')]"/>
|
||||
<filter string="Posted" domain="[('state','=','done')]"/>
|
||||
<field name="user_id"/>
|
||||
<field name="session_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Customer" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Salesman" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Session" domain="[]" context="{'group_by':'session_id'}"/>
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Order Month" domain="[]" context="{'group_by':'date_order'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_url" id="action_pos_pos">
|
||||
<field name="name">Start Point of Sale</field>
|
||||
<field name="url">/pos/web/</field>
|
||||
<field name="target">self</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<act_window
|
||||
id="act_pos_session_orders"
|
||||
name="Orders"
|
||||
src_model="pos.session"
|
||||
res_model="pos.order"
|
||||
context="{'search_default_session_id': active_id, 'default_session_id' : active_id }" />
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_session_form">
|
||||
<field name="name">pos.session.form.view</field>
|
||||
<field name="model">pos.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Point of Sale Session">
|
||||
<header>
|
||||
<button name="open_cb" type="object" string="Validate & Open Session" states="opening_control" class="oe_highlight"/>
|
||||
<button name="open_frontend_cb" type="object" string="Continue Selling" states="opened"
|
||||
class="oe_highlight"/>
|
||||
<button name="cashbox_control" type="workflow" string="End of Session"
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', '!=', 'opened')]}"
|
||||
class="oe_highlight" />
|
||||
<button name="close" type="workflow" string="Validate Closing & Post Entries"
|
||||
attrs="{'invisible' : ['|', ('cash_control', '=', True),('state', '!=', 'opened')]}"
|
||||
class="oe_highlight" />
|
||||
<button name="close" type="workflow" string="Validate Closing & Post Entries" states="closing_control"
|
||||
class="oe_highlight" />
|
||||
|
||||
<field name="state" widget="statusbar" statusbar_visible="opening_control,opened,closing_control,closed" nolabel="1" />
|
||||
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" name="%(action_pos_box_in)d"
|
||||
type="action" states="opened,closing_control" icon="fa-level-down">
|
||||
<div class="o_form_field o_stat_info">
|
||||
<span class="o_stat_text">Put</span>
|
||||
<span class="o_stat_text">Money In</span>
|
||||
</div>
|
||||
</button>
|
||||
<button class="oe_stat_button" name="%(action_pos_box_out)d"
|
||||
type="action" states="opened,closing_control" icon="fa-level-up">
|
||||
<div class="o_form_field o_stat_info">
|
||||
<span class="o_stat_text">Take</span>
|
||||
<span class="o_stat_text">Money Out</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="oe_title">
|
||||
Session:
|
||||
<field name="name" attrs="{'invisible': [('name','=','/')]}" class="oe_inline"/>
|
||||
</h1>
|
||||
<group>
|
||||
<field name="cash_control" invisible="1" />
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="config_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="start_at" attrs="{'invisible' : [('state', '=', 'opening_control')]}"/>
|
||||
<field name="stop_at" attrs="{'invisible' : [('state', '!=', 'closed')]}"/>
|
||||
</group>
|
||||
<newline/>
|
||||
|
||||
<div attrs="{'invisible' : [('cash_control', '=', False)]}">
|
||||
<group class="oe_subtotal_footer oe_right">
|
||||
<field name="cash_register_balance_start" readonly="1" string="Opening Balance" class="oe_subtotal_footer_separator" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<label for="cash_register_total_entry_encoding" attrs="{'invisible' : [('state', '=', 'opening_control')]}" string="+ Transactions" class="oe_force_bold oe_opening_total"/>
|
||||
<field name="cash_register_total_entry_encoding" nolabel="1" attrs="{'invisible' : [('state', '=', 'opening_control')]}" class="oe_bold oe_pos_total" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<label for="cash_register_balance_end" attrs="{'invisible' : [('state', '=', 'opening_control')]}" string="= Theoretical Closing Balance" class="oe_force_bold oe_opening_total"/>
|
||||
<field name="cash_register_balance_end" nolabel="1" attrs="{'invisible' : [('state', '=', 'opening_control')]}" class="oe_bold oe_pos_total" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
<div class="oe_clear"/>
|
||||
<div attrs="{'invisible' : ['|', ('cash_journal_id', '=', False), ('state', '!=', 'opening_control')]}" class="oe_view_nocontent" groups="point_of_sale.group_pos_manager">
|
||||
<p class="oe_view_nocontent_create">
|
||||
You can define another list of available currencies on the
|
||||
<i>Cash Registers</i> tab of the <b><field name="cash_journal_id" class="oe_inline"/></b>
|
||||
payment method.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<group class="oe_subtotal_footer oe_right" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', '=', 'opening_control')]}">
|
||||
<field name="cash_register_balance_end_real" string="Real Closing Balance" class="oe_subtotal_footer_separator" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
<group/>
|
||||
<group/>
|
||||
<group class="oe_subtotal_footer oe_right" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', '=', 'opening_control')]}">
|
||||
<field name="cash_register_difference" class="oe_subtotal_footer_separator oe_right oe_pos_total oe_pos_difference" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
</div>
|
||||
</group>
|
||||
<separator string="Summary by Payment Methods" attrs="{'invisible' : [('state', '=', 'opening_control')]}"/>
|
||||
<field name="statement_ids" attrs="{'invisible' : [('state', '=', 'opening_control')]}">
|
||||
<tree string="Statements">
|
||||
<field name="name" />
|
||||
<field name="journal_id" />
|
||||
<field name="balance_start" />
|
||||
<field name="total_entry_encoding" />
|
||||
<field name="balance_end_real" />
|
||||
<field name="difference" />
|
||||
<field name="currency_id" groups="base.group_multi_currency" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_session_tree">
|
||||
<field name="name">pos.session.tree.view</field>
|
||||
<field name="model">pos.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Point of Sale Session">
|
||||
<field name="config_id" />
|
||||
<field name="name" />
|
||||
<field name="user_id" />
|
||||
<field name="start_at" />
|
||||
<field name="stop_at" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_session_search">
|
||||
<field name="name">pos.session.search.view</field>
|
||||
<field name="model">pos.session</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Point of Sale Session">
|
||||
<field name="name" />
|
||||
<filter string="Open" domain="[('state', '=', 'opened')]" />
|
||||
<separator/>
|
||||
<filter string="Today" domain="[('start_at', '>=', datetime.datetime.combine(context_today(), datetime.time(0,0,0)))]" />
|
||||
<field name="config_id" />
|
||||
<field name="user_id" />
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="User" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
<filter string="Point of Sale" domain="[]" context="{'group_by': 'user_id'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_pos_session">
|
||||
<field name="name">Sessions</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pos.session</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_pos_session_search" />
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to start a new session.
|
||||
</p><p>
|
||||
A session is a period of time, usually one day, during which
|
||||
you sell through the point of sale. The user has to check the
|
||||
currencies in your cash registers at the beginning and the end
|
||||
of each session.
|
||||
</p><p>
|
||||
Note that you may use the menu <i>Your Session</i>
|
||||
to quickly open a new session.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_point_of_sale"
|
||||
action="action_pos_session"
|
||||
id="menu_pos_session_all"
|
||||
sequence="2"
|
||||
groups="group_pos_manager"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="product_template_action" model="ir.actions.act_window">
|
||||
<field name="name">Products</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">product.template</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,kanban</field>
|
||||
<field name="context" eval="{}"/>
|
||||
<field name="domain" eval="[('available_in_pos','<>',False)]"/>
|
||||
<field name="search_view_id" eval="False"/> <!-- Force empty -->
|
||||
<field name="view_id" eval="False"/> <!-- Force empty -->
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a new product.
|
||||
</p><p>
|
||||
You must define a product for everything you sell through
|
||||
the point of sale interface.
|
||||
</p><p>
|
||||
Do not forget to set the price and the point of sale category
|
||||
in which it should appear. If a product has no point of sale
|
||||
category, you can not sell it through the point of sale
|
||||
interface.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem
|
||||
action="product_template_action"
|
||||
id="menu_pos_products"
|
||||
parent="menu_point_of_sale" sequence="3"/>
|
||||
|
||||
<record id="product_template_form_view" model="ir.ui.view">
|
||||
<field name="name">product.template.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="sale_condition" position="after">
|
||||
<group name="pos" string="Point of Sale">
|
||||
<field name="available_in_pos"/>
|
||||
<field name="pos_categ_id" attrs="{'invisible': [('available_in_pos', '=', False)]}"/>
|
||||
<field name="to_weight" attrs="{'invisible': [('available_in_pos', '=', False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
<record id="product_template_tree_view" model="ir.ui.view">
|
||||
<field name="name">product.template.tree.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_tree_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="before">
|
||||
<field name="sequence" widget="handle" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -7,27 +7,10 @@ from openerp.osv import osv, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class account_journal(osv.osv):
|
||||
_inherit = 'account.journal'
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
|
||||
if not context:
|
||||
context = {}
|
||||
session_id = context.get('pos_session_id', False) or False
|
||||
|
||||
if session_id:
|
||||
session = self.pool.get('pos.session').browse(cr, uid, session_id, context=context)
|
||||
|
||||
if session:
|
||||
journal_ids = [journal.id for journal in session.config_id.journal_ids]
|
||||
args += [('id', 'in', journal_ids)]
|
||||
|
||||
return super(account_journal, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
|
||||
|
||||
|
||||
class pos_make_payment(osv.osv_memory):
|
||||
_name = 'pos.make.payment'
|
||||
_description = 'Point of Sale Payment'
|
||||
|
||||
def check(self, cr, uid, ids, context=None):
|
||||
"""Check the order:
|
||||
if the order is not paid: continue payment,
|
||||
|
||||
Reference in New Issue
Block a user