diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py
index fba1bd8736f..e6f356e0a44 100644
--- a/addons/account/__openerp__.py
+++ b/addons/account/__openerp__.py
@@ -100,6 +100,7 @@ module named account_voucher.
'wizard/account_reconcile_partner_process_view.xml',
'wizard/account_automatic_reconcile_view.xml',
'wizard/account_financial_report_view.xml',
+ 'wizard/pos_box.xml',
'project/wizard/project_account_analytic_line_view.xml',
'account_end_fy.xml',
'account_invoice_view.xml',
@@ -145,8 +146,8 @@ module named account_voucher.
'test/account_use_model.yml',
'test/account_validate_account_move.yml',
'test/account_fiscalyear_close.yml',
- 'test/account_bank_statement.yml',
- 'test/account_cash_statement.yml',
+ #'test/account_bank_statement.yml',
+ #'test/account_cash_statement.yml',
'test/test_edi_invoice.yml',
'test/account_report.yml',
'test/account_fiscalyear_close_state.yml', #last test, as it will definitively close the demo fiscalyear
diff --git a/addons/account/account.py b/addons/account/account.py
index 75bfc891980..df446abc3bc 100644
--- a/addons/account/account.py
+++ b/addons/account/account.py
@@ -739,6 +739,10 @@ class account_journal(osv.osv):
'entry_posted': fields.boolean('Skip \'Draft\' State for Manual Entries', help='Check this box if you don\'t want new journal entries to pass through the \'draft\' state and instead goes directly to the \'posted state\' without any manual validation. \nNote that journal entries that are automatically created by the system are always skipping that state.'),
'company_id': fields.many2one('res.company', 'Company', required=True, select=1, help="Company related to this journal"),
'allow_date':fields.boolean('Check Date in Period', help= 'If set to True then do not accept the entry if the entry date is not into the period dates'),
+
+ 'profit_account_id' : fields.many2one('account.account', 'Profit Account'),
+ 'loss_account_id' : fields.many2one('account.account', 'Loss Account'),
+ 'internal_account_id' : fields.many2one('account.account', 'Internal Transfers Account', select=1),
}
_defaults = {
diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py
index 5601179e0fc..889721d4fa5 100644
--- a/addons/account/account_bank_statement.py
+++ b/addons/account/account_bank_statement.py
@@ -26,24 +26,18 @@ from tools.translate import _
import decimal_precision as dp
class account_bank_statement(osv.osv):
-
def create(self, cr, uid, vals, context=None):
- seq = 0
if 'line_ids' in vals:
- new_line_ids = []
- for line in vals['line_ids']:
- seq += 1
- line[2]['sequence'] = seq
+ for idx, line in enumerate(vals['line_ids']):
+ line[2]['sequence'] = idx + 1
return super(account_bank_statement, self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
res = super(account_bank_statement, self).write(cr, uid, ids, vals, context=context)
account_bank_statement_line_obj = self.pool.get('account.bank.statement.line')
for statement in self.browse(cr, uid, ids, context):
- seq = 0
- for line in statement.line_ids:
- seq += 1
- account_bank_statement_line_obj.write(cr, uid, [line.id], {'sequence': seq}, context=context)
+ for idx, line in enumerate(statement.line_ids):
+ account_bank_statement_line_obj.write(cr, uid, [line.id], {'sequence': idx + 1}, context=context)
return res
def _default_journal_id(self, cr, uid, context=None):
@@ -51,13 +45,12 @@ class account_bank_statement(osv.osv):
context = {}
journal_pool = self.pool.get('account.journal')
journal_type = context.get('journal_type', False)
- journal_id = False
company_id = self.pool.get('res.company')._company_default_get(cr, uid, 'account.bank.statement',context=context)
if journal_type:
ids = journal_pool.search(cr, uid, [('type', '=', journal_type),('company_id','=',company_id)])
if ids:
- journal_id = ids[0]
- return journal_id
+ return ids[0]
+ return False
def _end_balance(self, cursor, user, ids, name, attr, context=None):
res_currency_obj = self.pool.get('res.currency')
@@ -379,14 +372,22 @@ class account_bank_statement(osv.osv):
account_move_obj.unlink(cr, uid, ids, context)
done.append(st.id)
return self.write(cr, uid, done, {'state':'draft'}, context=context)
-
- def onchange_journal_id(self, cr, uid, statement_id, journal_id, context=None):
+
+ def _compute_balance_end_real(self, cr, uid, journal_id, context=None):
+
cr.execute('SELECT balance_end_real \
FROM account_bank_statement \
WHERE journal_id = %s AND NOT state = %s \
ORDER BY date DESC,id DESC LIMIT 1', (journal_id, 'draft'))
res = cr.fetchone()
- balance_start = res and res[0] or 0.0
+
+ print "res: %r" % (res,)
+
+ return res and res[0] or 0.0
+
+ def onchange_journal_id(self, cr, uid, statement_id, journal_id, context=None):
+ balance_start = self._compute_balance_end_real(cr, uid, journal_id, context=context)
+
journal_data = self.pool.get('account.journal').read(cr, uid, journal_id, ['default_debit_account_id', 'company_id'], context=context)
account_id = journal_data['default_debit_account_id']
company_id = journal_data['company_id']
diff --git a/addons/account/account_cash_statement.py b/addons/account/account_cash_statement.py
index f19c1e1c639..9cec8dfc723 100644
--- a/addons/account/account_cash_statement.py
+++ b/addons/account/account_cash_statement.py
@@ -43,24 +43,27 @@ class account_cashbox_line(osv.osv):
"""
res = {}
for obj in self.browse(cr, uid, ids, context=context):
- res[obj.id] = obj.pieces * obj.number
+ res[obj.id] = {
+ 'subtotal_opening' : obj.pieces * obj.number_opening,
+ 'subtotal_closing' : obj.pieces * obj.number_closing,
+ }
return res
- def on_change_sub(self, cr, uid, ids, pieces, number, *a):
+ def on_change_sub_opening(self, cr, uid, ids, pieces, number, *a):
+ """ Compute the subtotal for the opening """
+ return {'value' : {'subtotal_opening' : (pieces * number) or 0.0 }}
- """ Calculates Sub total on change of number
- @param pieces: Names of fields.
- @param number:
- """
- sub = pieces * number
- return {'value': {'subtotal': sub or 0.0}}
+ def on_change_sub_closing(self, cr, uid, ids, pieces, number, *a):
+ """ Compute the subtotal for the closing """
+ return {'value' : {'subtotal_closing' : (pieces * number) or 0.0 }}
_columns = {
- 'pieces': fields.float('Values', digits_compute=dp.get_precision('Account')),
- 'number': fields.integer('Number'),
- 'subtotal': fields.function(_sub_total, string='Sub Total', type='float', digits_compute=dp.get_precision('Account')),
- 'starting_id': fields.many2one('account.bank.statement', ondelete='cascade'),
- 'ending_id': fields.many2one('account.bank.statement', ondelete='cascade'),
+ 'pieces': fields.float('Unit of Currency', digits_compute=dp.get_precision('Account')),
+ 'number_opening' : fields.integer('Number of Units', help='Opening Unit Numbers'),
+ 'number_closing' : fields.integer('Number of Units', help='Closing Unit Numbers'),
+ 'subtotal_opening': fields.function(_sub_total, string='Subtotal Opening', type='float', digits_compute=dp.get_precision('Account'), multi='subtotal'),
+ 'subtotal_closing': fields.function(_sub_total, string='Subtotal Closing', type='float', digits_compute=dp.get_precision('Account'), multi='subtotal'),
+ 'bank_statement_id' : fields.many2one('account.bank.statement', ondelete='cascade'),
}
account_cashbox_line()
@@ -78,16 +81,14 @@ class account_cash_statement(osv.osv):
"""
res = {}
for statement in self.browse(cr, uid, ids, context=context):
- amount_total = 0.0
-
if statement.journal_id.type not in('cash'):
continue
-
- for line in statement.starting_details_ids:
- amount_total+= line.pieces * line.number
res[statement.id] = {
- 'balance_start': amount_total
+ 'balance_start': sum((line.pieces * line.number_opening
+ for line in statement.details_ids), 0.0)
}
+
+ print "_get_starting_balance: %r" % (res,)
return res
def _balance_end_cash(self, cr, uid, ids, name, arg, context=None):
@@ -98,10 +99,9 @@ class account_cash_statement(osv.osv):
"""
res = {}
for statement in self.browse(cr, uid, ids, context=context):
- amount_total = 0.0
- for line in statement.ending_details_ids:
- amount_total += line.pieces * line.number
- res[statement.id] = amount_total
+ res[statement.id] = sum((line.pieces * line.number_closing
+ for line in statement.details_ids), 0.0)
+ print "_balance_end_cash: %r" % (res,)
return res
def _get_sum_entry_encoding(self, cr, uid, ids, name, arg, context=None):
@@ -111,13 +111,11 @@ class account_cash_statement(osv.osv):
@param arg: User defined arguments
@return: Dictionary of values.
"""
- res2 = {}
+ res = {}
for statement in self.browse(cr, uid, ids, context=context):
- encoding_total=0.0
- for line in statement.line_ids:
- encoding_total += line.amount
- res2[statement.id] = encoding_total
- return res2
+ res[statement.id] = sum((line.amount for line in statement.line_ids), 0.0)
+ print "_get_sum_entry_encoding: %r" % (res,)
+ return res
def _get_company(self, cr, uid, context=None):
user_pool = self.pool.get('res.users')
@@ -190,33 +188,84 @@ class account_cash_statement(osv.osv):
result[line.statement_id.id] = True
return result.keys()
+ def _compute_difference(self, cr, uid, ids, fieldnames, args, context=None):
+ result = dict.fromkeys(ids, 0.0)
+
+ for obj in self.browse(cr, uid, ids, context=context):
+ result[obj.id] = obj.balance_end - obj.balance_end_cash
+
+ return result
+
_columns = {
'total_entry_encoding': fields.function(_get_sum_entry_encoding, string="Cash Transaction", help="Total cash transactions",
store = {
- 'account.bank.statement': (lambda self, cr, uid, ids, c={}: ids, ['line_ids','move_line_ids'], 10),
+ 'account.bank.statement': (lambda self, cr, uid, ids, context=None: ids, ['line_ids','move_line_ids'], 10),
'account.bank.statement.line': (_get_statement, ['amount'], 10),
}),
'closing_date': fields.datetime("Closed On"),
- 'balance_end_cash': fields.function(_balance_end_cash, store=True, string='Closing Balance', help="Closing balance based on cashBox"),
- 'starting_details_ids': fields.one2many('account.cashbox.line', 'starting_id', string='Opening Cashbox'),
- 'ending_details_ids': fields.one2many('account.cashbox.line', 'ending_id', string='Closing Cashbox'),
+ 'balance_end_cash': fields.function(_balance_end_cash, store=False, string='Closing Balance', help="Closing balance based on cashBox"),
+ 'details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='CashBox Lines'),
'user_id': fields.many2one('res.users', 'Responsible', required=False),
+ 'difference' : fields.function(_compute_difference, method=True, string="Difference", type="float"),
}
+
_defaults = {
'state': 'draft',
- 'date': lambda self,cr,uid,context={}: context.get('date', time.strftime("%Y-%m-%d %H:%M:%S")),
+ 'date': lambda self, cr, uid, context={}: context.get('date', time.strftime("%Y-%m-%d %H:%M:%S")),
'user_id': lambda self, cr, uid, context=None: uid,
- 'starting_details_ids': _get_cash_open_box_lines,
- 'ending_details_ids': _get_default_cash_close_box_lines
- }
+ }
+
+ def check_opening_journal(self, cr, uid, ids, context=None):
+ """
+ This constraint will check than the journal is not used twice in the system,
+ to avoid a concurrency opening of this journal.
+ """
+ for cash in self.browse(cr, uid, ids, context=None):
+ domain = [
+ ('id', '!=', cash.id),
+ ('journal_id', '=', cash.journal_id.id),
+ ('journal_id.type', '=', 'cash'),
+ ('state', 'in', ('open',)),
+ ]
+ count = self.search_count(cr, uid, domain, context=context)
+
+ if count:
+ return False
+ return True
+
+ _constraints = [
+ #(check_opening_journal, "The selected journal has been opened !", ['journal_id']),
+ ]
def create(self, cr, uid, vals, context=None):
if self.pool.get('account.journal').browse(cr, uid, vals['journal_id'], context=context).type == 'cash':
amount_total = 0.0
- for line in vals.get('starting_details_ids',[]):
+ for line in vals.get('details_ids',[]):
+ print "line: %r" % (line,)
if line and len(line)==3 and line[2]:
- amount_total+= line[2]['pieces'] * line[2]['number']
+ # FIXME: If there is no piece # does not work with GTK
+ amount_total+= line[2].get('pieces', 0) * line[2]['number_opening']
+
vals.update(balance_start= amount_total)
+ vals.update(balance_end_real=self._compute_balance_end_real(cr, uid, vals['journal_id'], context=context))
+
+ details = vals.get('details_ids')
+ if not details:
+ result = self.onchange_journal_id(cr, uid, None, vals['journal_id'], context=context)
+ vals['details_ids'] = []
+
+ for value in (result['value']['details_ids'] or []):
+ print "value: %r" % (value,)
+ nested_values = {
+ 'number_closing' : False,
+ 'number_opening' : False,
+ 'pieces' : value['pieces'],
+ 'subtotal_closing' : False,
+ 'subtotal_opening' : False,
+ }
+
+ vals['details_ids'].append([0, False, nested_values])
+
return super(account_cash_statement, self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
@@ -245,19 +294,43 @@ class account_cash_statement(osv.osv):
@param journal_id: Changed journal_id
@return: Dictionary of changed values
"""
- res = {}
balance_start = 0.0
- if not journal_id:
- res.update({
- 'balance_start': balance_start
- })
- return res
+ if journal_id:
+ count = self.search_count(cr, uid, [('journal_id', '=', journal_id),('state', '=', 'open')], context=None)
+ if 0: # count:
+ journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
+ raise osv.except_osv(_('Error !'), (_('The Account Journal %s is opened by an other Cash Register !') % (journal.name,)))
+ else:
+ values = super(account_cash_statement, self).onchange_journal_id(cr, uid, statement_id, journal_id, context=context)
+ proxy_line = self.pool.get('account.cashbox.line')
+
+ values.setdefault('value', {})
+ values['value']['details_ids'] = []
+ values['value']['balance_end_real'] = self._compute_balance_end_real(cr, uid, journal_id, context=context)
+ values['value']['balance_start'] = 0.0
+
+ journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
+
+ for line in journal.cashbox_line_ids:
+ values['value']['details_ids'].append({'pieces' : line.pieces})
+
+ return values
+ else:
+ return {
+ 'value' : {
+ 'balance_start': balance_start,
+ 'details_ids' : [],
+ }
+ }
return super(account_cash_statement, self).onchange_journal_id(cr, uid, statement_id, journal_id, context=context)
def _equal_balance(self, cr, uid, cash_id, context=None):
statement = self.browse(cr, uid, cash_id, context=context)
self.write(cr, uid, [cash_id], {'balance_end_real': statement.balance_end})
- statement.balance_end_real = statement.balance_end
+ print "balance_end_real: %r" % (statement.balance_end,)
+ print "balance_end: %r" % (statement.balance_end,)
+ print "balance_end_cash: %r" % (statement.balance_end_cash,)
+
if statement.balance_end != statement.balance_end_cash:
return False
return True
@@ -317,16 +390,64 @@ class account_cash_statement(osv.osv):
def button_confirm_cash(self, cr, uid, ids, context=None):
super(account_cash_statement, self).button_confirm_bank(cr, uid, ids, context=context)
+ absl_proxy = self.pool.get('account.bank.statement.line')
+
+ TABLES = (('Profit', 'profit_account_id'), ('Loss', 'loss_account_id'),)
+
+ for obj in self.browse(cr, uid, ids, context=context):
+ if obj.difference == 0.0:
+ continue
+
+ for item_label, item_account in TALBES:
+ if getattr(obj.journal_id, item_account):
+ raise osv.except_osv(_('Error !'),
+ _('There is no %s Account on the Journal %s') % (item_label, obj.journal_id.name,))
+
+ is_profit = obj.difference < 0.0
+
+ account = getattr(obj.journal_id, TABLES[is_profit][1])
+
+ values = {
+ 'statement_id' : obj.id,
+ 'journal_id' : obj.journal_id.id,
+ 'account_id' : account.id,
+ 'amount' : obj.difference,
+ 'name' : 'Exceptional %s' % TABLES[is_profit][0],
+ }
+
+ absl_proxy.create(cr, uid, values, context=context)
+
return self.write(cr, uid, ids, {'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")}, context=context)
def button_cancel(self, cr, uid, ids, context=None):
cash_box_line_pool = self.pool.get('account.cashbox.line')
super(account_cash_statement, self).button_cancel(cr, uid, ids, context=context)
for st in self.browse(cr, uid, ids, context):
- for end in st.ending_details_ids:
- cash_box_line_pool.write(cr, uid, [end.id], {'number': 0})
+ for end in st.details_ids:
+ cash_box_line_pool.write(cr, uid, [end.id], {'number_closing': 0})
return True
account_cash_statement()
+class account_journal(osv.osv):
+ _inherit = 'account.journal'
+
+ _columns = {
+ 'cashbox_line_ids' : fields.one2many('account.journal.cashbox.line', 'journal_id', 'CashBox'),
+ }
+
+account_journal()
+
+class account_journal_cashbox_line(osv.osv):
+ _name = 'account.journal.cashbox.line'
+ _rec_name = 'value'
+ _columns = {
+ 'pieces': fields.float('Values', digits_compute=dp.get_precision('Account')),
+ 'journal_id' : fields.many2one('account.journal', 'Journal', required=True, select=1),
+ }
+
+ _order = 'pieces asc'
+
+account_journal_cashbox_line()
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml
index 7829dc91e03..273035fbc2a 100644
--- a/addons/account/account_view.xml
+++ b/addons/account/account_view.xml
@@ -504,6 +504,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2597,7 +2610,7 @@ action = pool.get('res.config').next(cr, uid, [], context)
-
+
@@ -2615,7 +2628,7 @@ action = pool.get('res.config').next(cr, uid, [], context)
-
+
@@ -2651,32 +2664,16 @@ action = pool.get('res.config').next(cr, uid, [], context)
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
@@ -2687,18 +2684,20 @@ action = pool.get('res.config').next(cr, uid, [], context)
-
+
-
-
+
+
+
+
diff --git a/addons/account/wizard/__init__.py b/addons/account/wizard/__init__.py
index a78cf12050b..87a053d558c 100644
--- a/addons/account/wizard/__init__.py
+++ b/addons/account/wizard/__init__.py
@@ -64,6 +64,8 @@ import account_report_account_balance
import account_change_currency
+import pos_box;
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/account/wizard/pos_box.py b/addons/account/wizard/pos_box.py
new file mode 100644
index 00000000000..a9e752397e0
--- /dev/null
+++ b/addons/account/wizard/pos_box.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+from osv import osv, fields
+import decimal_precision as dp
+
+class CashBox(osv.osv_memory):
+ _register = False
+ _columns = {
+ 'name' : fields.char('Reason', size=64, required=True),
+ # Attention, we don't set a domain, because there is a journal_type key
+ # in the context of the action
+ 'amount' : fields.float('Amount',
+ digits_compute = dp.get_precision('Account'),
+ required=True),
+ }
+
+ def run(self, cr, uid, ids, context=None):
+ if not context:
+ context = dict()
+
+ active_model = context.get('active_model', False) or False
+ active_ids = context.get('active_ids', []) or []
+
+ records = self.pool.get(active_model).browse(cr, uid, active_ids, context=context)
+
+ return self._run(cr, uid, ids, records, context=None)
+
+ def _run(self, cr, uid, ids, records, context=None):
+ for box in self.browse(cr, uid, ids, context=context):
+ for record in records:
+ if not record.journal_id.internal_account_id:
+ raise osv.except_osv(_('Error !'),
+ _('Please check that internal account is set to %s') % (record.journal_id.name,))
+
+ self._create_bank_statement_line(cr, uid, box, record, context=context)
+
+ return {}
+
+
+class CashBoxIn(CashBox):
+ _name = 'cash.box.in'
+
+ _columns = CashBox._columns.copy()
+ _columns.update({
+ 'ref' : fields.char('Reference', size=32),
+ })
+
+ def _create_bank_statement_line(self, cr, uid, box, record, context=None):
+ absl_proxy = self.pool.get('account.bank.statement.line')
+
+ values = {
+ 'statement_id' : record.id,
+ 'journal_id' : record.journal_id.id,
+ 'account_id' : record.journal_id.internal_account_id.id,
+ 'amount' : box.amount or 0.0,
+ 'ref' : "%s" % (box.ref or ''),
+ 'name' : box.name,
+ }
+
+ return absl_proxy.create(cr, uid, values, context=context)
+
+CashBoxIn()
+
+class CashBoxOut(CashBox):
+ _name = 'cash.box.out'
+
+ def _create_bank_statement_line(self, cr, uid, box, record, context=None):
+ absl_proxy = self.pool.get('account.bank.statement.line')
+
+ amount = box.amount or 0.0
+ values = {
+ 'statement_id' : record.id,
+ 'journal_id' : record.journal_id.id,
+ 'account_id' : record.journal_id.internal_account_id.id,
+ 'amount' : -amount if amount > 0.0 else amount,
+ 'name' : box.name,
+ }
+
+ return absl_proxy.create(cr, uid, values, context=context)
+
+CashBoxOut()
diff --git a/addons/account/wizard/pos_box.xml b/addons/account/wizard/pos_box.xml
new file mode 100644
index 00000000000..6136cca06ad
--- /dev/null
+++ b/addons/account/wizard/pos_box.xml
@@ -0,0 +1,62 @@
+
+
+
+
+ cash_box_in
+ cash.box.in
+ form
+
+
+
+
+
+
+
+
+ cash_box_out
+ cash.box.out
+ form
+
+
+
+
+
+
+
+
diff --git a/addons/point_of_sale/__init__.py b/addons/point_of_sale/__init__.py
index be73103125f..4d35174ce6b 100644
--- a/addons/point_of_sale/__init__.py
+++ b/addons/point_of_sale/__init__.py
@@ -24,5 +24,7 @@ import account_bank_statement
import wizard
import report
+import controllers
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py
index 2c356ab4f31..c97ffedf046 100644
--- a/addons/point_of_sale/__openerp__.py
+++ b/addons/point_of_sale/__openerp__.py
@@ -51,14 +51,13 @@ Main features :
'wizard/pos_discount.xml',
'wizard/pos_open_statement.xml',
'wizard/pos_close_statement.xml',
- 'wizard/pos_box_entries.xml',
'wizard/pos_payment_report_user_view.xml',
- 'wizard/pos_box_out.xml',
'wizard/pos_sales_user.xml',
'wizard/pos_receipt_view.xml',
'wizard/pos_payment_report_user.xml',
'wizard/pos_payment_report.xml',
'wizard/pos_payment.xml',
+ 'wizard/pos_box.xml',
'point_of_sale_report.xml',
'point_of_sale_view.xml',
'report/pos_order_report_view.xml',
@@ -92,5 +91,6 @@ Main features :
],
'css': ['static/src/css/pos.css'],
'qweb': ['static/src/xml/pos.xml'],
+ 'auto_install': True,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/point_of_sale/account_bank_statement.py b/addons/point_of_sale/account_bank_statement.py
index 9cc39ced45c..ab03b61ba43 100644
--- a/addons/point_of_sale/account_bank_statement.py
+++ b/addons/point_of_sale/account_bank_statement.py
@@ -25,13 +25,15 @@ from osv import fields, osv
class account_journal(osv.osv):
_inherit = 'account.journal'
_columns = {
- 'auto_cash': fields.boolean('Automatic Opening', help="This field authorize the automatic creation of the cashbox, without control of the initial balance."),
- 'check_dtls': fields.boolean('Control Balance Before Closing', help="This field authorize Validation of Cashbox without controlling the closing balance."),
'journal_user': fields.boolean('PoS Payment Method', help="Check this box if this journal define a payment method that can be used in point of sales."),
+
+ 'opening_control': fields.boolean('Opening Control', help="If you want the journal should be control at opening, check this option"),
+ 'closing_control': fields.boolean('Closing Control', help="If you want the journal should be control at closing, check this option"),
+
}
_defaults = {
- 'check_dtls': False,
- 'auto_cash': True,
+ 'opening_control' : True,
+ 'closing_control' : True,
}
account_journal()
@@ -39,14 +41,14 @@ account_journal()
class account_cash_statement(osv.osv):
_inherit = 'account.bank.statement'
- def _equal_balance(self, cr, uid, cash_id, context=None):
- statement = self.browse(cr, uid, cash_id, context=context)
- if not statement.journal_id.check_dtls:
- return True
- if statement.journal_id.check_dtls and (statement.balance_end != statement.balance_end_cash):
- return False
- else:
- return True
+ #def _equal_balance(self, cr, uid, cash_id, context=None):
+ # statement = self.browse(cr, uid, cash_id, context=context)
+ # if not statement.journal_id.check_dtls:
+ # return True
+ # if statement.journal_id.check_dtls and (statement.balance_end != statement.balance_end_cash):
+ # return False
+ # else:
+ # return True
def _get_cash_open_box_lines(self, cr, uid, context=None):
res = super(account_cash_statement,self)._get_cash_open_box_lines(cr, uid, context)
diff --git a/addons/point_of_sale/account_statement_view.xml b/addons/point_of_sale/account_statement_view.xml
index a1a0fa44b39..9bc039a1852 100644
--- a/addons/point_of_sale/account_statement_view.xml
+++ b/addons/point_of_sale/account_statement_view.xml
@@ -12,8 +12,8 @@
-
-
+
+
diff --git a/addons/point_of_sale/controllers.py b/addons/point_of_sale/controllers.py
new file mode 100644
index 00000000000..9cea329fcb1
--- /dev/null
+++ b/addons/point_of_sale/controllers.py
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+import logging
+
+try:
+ import openerp.addons.web.common.http as openerpweb
+except ImportError:
+ import web.common.http as openerpweb
+
+class PointOfSaleController(openerpweb.Controller):
+ _cp_path = '/pos'
+
+ @openerpweb.jsonrequest
+ def dispatch(self, request, iface, **kwargs):
+ method = 'iface_%s' % iface
+ return getattr(self, method)(request, **kwargs)
+
+ def iface_light(self, request, status):
+ return True
+
+ @openerpweb.jsonrequest
+ def scan_item_success(self, request):
+ """
+ A product has been scanned with success
+ """
+ return False
+
+ @openerpweb.jsonrequest
+ def scan_item_error_unrecognized(self, request):
+ """
+ A product has been scanned without success
+ """
+ return False
+
+ @openerpweb.jsonrequest
+ def do_help(self, request, status):
+ if status == 1:
+ return help_needed(request)
+ else:
+ return help_cancelled(request)
+
+ @openerpweb.jsonrequest
+ def help_needed(self, request):
+ """
+ The user wants an help (ex: light is on)
+ """
+ return self.signal_help(request, status=True)
+
+ @openerpweb.jsonrequest
+ def help_cancelled(self, request):
+ """
+ The user stops the help request
+ """
+ return self.signal_help(request, status=False)
+
+ #@openerpweb.jsonrequest
+ #def weighting_start(self, request):
+ # return False
+
+ #@openerpweb.jsonrequest
+ #def weighting_read_kg(self, request):
+ # return 0.0
+
+ #@openerpweb.jsonrequest
+ #def weighting_end(self, request):
+ # return False
+
+ #openerpweb.jsonrequest
+ def do_weighting(self, request):
+ # Start the weighting
+
+ # Wait for 10 sec
+ # IDEA: Thread, Signal ?
+
+ # return a dict with the value or the error
+
+ return {'weight' : 0.5}
+
+
+ def do_payment(self, request, price, method, info):
+ #return {'status' : 'ACCEPTED', 'reason' : ''}
+ return {'status' : 'REFUSED', 'reason' : 'Payment blocked'}
+
+ #@openerpweb.jsonrequest
+ #def payment_request(self, request, price, method, info):
+ # """
+ # The PoS will activate the method payment
+ # """
+ # return False
+
+ #@openerpweb.jsonrequest
+ #def is_payment_accepted(self, request):
+ # return False
+
+ #@openerpweb.jsonrequest
+ #def payment_cancelled(self, request):
+ # return False
+
+ @openerpweb.jsonrequest
+ def transaction_start(self, request):
+ return False
+
+ @openerpweb.jsonrequest
+ def transaction_end(self, request):
+ return False
+
+ @openerpweb.jsonrequest
+ def cashier_mode_activated(self, request):
+ return False
+
+ @openerpweb.jsonrequest
+ def cashier_mode_deactivated(self, request):
+ return False
+
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py
index 5af78b131b2..877da092468 100644
--- a/addons/point_of_sale/point_of_sale.py
+++ b/addons/point_of_sale/point_of_sale.py
@@ -33,6 +33,244 @@ import decimal_precision as dp
_logger = logging.getLogger(__name__)
+class pos_config(osv.osv):
+ _name = 'pos.config'
+
+ POS_CONFIG_STATE = [('draft', 'Draft'),('active', 'Active'),('inactive', 'Inactive'),('deprecated', 'Deprecated')]
+
+ _columns = {
+ 'name' : fields.char('Name', size=32, select=1, required=True),
+ 'journal_ids' : fields.many2many('account.journal', 'pos_config_journal_rel', 'pos_config_id', 'journal_id', 'Payment Methods', domain="[('journal_user', '=', True )]"),
+ 'shop_id' : fields.many2one('sale.shop', 'Shop', required=True, select=1),
+ 'journal_id' : fields.many2one('account.journal', 'Journal', required=True, select=1, domain=[('type', '=', 'sale')]),
+ 'profit_account_id' : fields.many2one('account.account', 'Profit Account', required=True, select=1),
+ 'loss_account_id' : fields.many2one('account.account', 'Loss Account', required=True, select=1),
+
+ 'authorized_cashbox_diff' : fields.integer('Authorized Cashbox Difference (%)'),
+ 'authorized_cashbox_diff_fixed' : fields.integer('Authorized Cashbox Difference (Fixed Amount)'),
+
+
+ 'iface_self_checkout' : fields.boolean('Self Checkout Mode'),
+ 'iface_websql' : fields.boolean('WebSQL (to store data)'),
+ 'iface_led' : fields.boolean('LED Interface'),
+ 'iface_cashdrawer' : fields.boolean('Cashdrawer Interface'),
+ 'iface_payment_terminal' : fields.boolean('Payment Terminal Interface'),
+ 'iface_electronic_scale' : fields.boolean('Electronic Scale Interface'),
+ 'iface_barscan' : fields.boolean('BarScan Interface'),
+ 'iface_vkeyboard' : fields.boolean('Virtual KeyBoard Interface'),
+
+ 'state' : fields.selection(POS_CONFIG_STATE, 'State', required=True, readonly=True),
+
+ 'sequence_id' : fields.many2one('ir.sequence', 'Sequence', readonly=True),
+ # Add a sequence when we create a new pos.config object
+ 'user_id' : fields.many2one('res.users', 'User'),
+
+ }
+
+ _defaults = {
+ 'state' : 'draft',
+ 'user_id' : lambda obj, cr, uid, context: uid,
+ }
+
+ def _check_only_one_cash_journal(self, cr, uid, ids, context=None):
+ for record in self.browse(cr, uid, ids, context=context):
+ has_cash_journal = False
+
+ for journal in record.journal_ids:
+ if journal.type == 'cash':
+ if has_cash_journal:
+ return False
+ else:
+ has_cash_journal = True
+ return True
+
+ _constraints = [
+ (_check_only_one_cash_journal, "You should have only one Cash Journal !", ['journal_id']),
+ ]
+
+ def set_draft(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state' : 'draft'}, context=context)
+
+ 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):
+ proxy = self.pool.get('ir.sequence.type')
+
+ sequence_values = dict(
+ code='pos_%s_sequence' % values['name'].lower(),
+ name='POS %s Sequence' % values['name'],
+ )
+
+ proxy.create(cr, uid, sequence_values, context=context)
+
+ proxy = self.pool.get('ir.sequence')
+
+ sequence_values = dict(
+ code='pos_%s_sequence' % values['name'].lower(),
+ name='POS %s Sequence' % values['name'],
+ padding=4,
+ prefix="%s/%%(year)s/%%(month)s/%%(day)s/" % values['name'],
+ )
+ sequence_id = proxy.create(cr, uid, sequence_values, context=context)
+
+ values['sequence_id'] = sequence_id
+ return super(pos_config, self).create(cr, uid, values, context=context)
+
+ def write(self, cr, uid, ids, values, context=None):
+ for obj in self.browse(cr, uid, ids, context=context):
+ if obj.sequence_id and values.get('name', False):
+ prefixes = obj.sequence_id.prefix.split('/')
+ if len(prefixes) >= 4 and prefixes[0] == obj.name:
+ prefixes[0] = values['name']
+
+ sequence_values = dict(
+ code='pos_%s_sequence' % values['name'].lower(),
+ name='POS %s Sequence' % values['name'],
+ prefix="/".join(prefixes),
+ )
+ obj.sequence_id.write(sequence_values)
+
+ return super(pos_config, self).write(cr, uid, ids, 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)
+
+pos_config()
+
+class pos_session(osv.osv):
+ _name = 'pos.session'
+
+ POS_SESSION_STATE = [('new', 'New'),('opened', 'Opened'),('closed', 'Closed'),('posted', 'Posted')]
+
+ _columns = {
+ 'config_id' : fields.many2one('pos.config', 'PoS', required=True, select=1, domain="[('state', '=', 'active')]"),
+
+ 'name' : fields.char('Session Sequence', size=32, required=True, select=1, readonly=1),
+ 'user_id' : fields.many2one('res.users', 'User', required=True, select=1),
+ 'start_at' : fields.datetime('Opening Date'), #, readonly=True),
+ 'stop_at' : fields.datetime('Closing Date'),
+
+ 'state' : fields.selection(POS_SESSION_STATE, 'State', required=True, readonly=True, select=1),
+
+ 'cash_register_id' : fields.many2one('account.bank.statement', 'Bank Account Statement'),
+
+ 'details_ids' : fields.related('cash_register_id', 'details_ids',
+ type='one2many', relation='account.cashbox.line',
+ string='CashBox Lines'),
+ }
+
+ _defaults = {
+ 'name' : '/',
+ 'user_id' : lambda obj, cr, uid, context: uid,
+ 'state' : 'new',
+ }
+
+ _sql_constraints = [
+ ('uniq_name', 'unique(name)', "The name of this POS Session must be unique !"),
+ ]
+
+ def _create_cash_register(self, cr, uid, pos_config, name, context=None):
+ if not pos_config:
+ return False
+
+ proxy = self.pool.get('account.bank.statement')
+
+ journal_id = False
+ for journal in pos_config.journal_ids:
+ if journal.type == 'cash':
+ journal_id = journal.id
+ break
+
+ if not journal_id:
+ return False
+
+ values = {
+ 'name' : name,
+ 'journal_id' : journal_id,
+ }
+ cash_register_id = proxy.create(cr, uid, values, context=context)
+
+ return cash_register_id
+
+ def create(self, cr, uid, values, context=None):
+ config_id = values.get('config_id', False) or False
+
+ if config_id:
+ pos_config = self.pool.get('pos.config').browse(cr, uid, config_id, context=context)
+ name = pos_config.sequence_id._next()
+ values.update(
+ name=name,
+ cash_register_id=self._create_cash_register(cr, uid, pos_config, name, context=context),
+ )
+ else:
+ raise osv.except_osv(_('Error!'), _('There is no POS Config attached to this POS Session'))
+
+ return super(pos_session, self).create(cr, uid, values, context=context)
+
+ def wkf_action_open(self, cr, uid, ids, context=None):
+ # si pas de date start_at, je balance une date, sinon on utilise celle de l'utilisateur
+ 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, context=context)
+
+ return True
+
+ def wkf_action_close(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state' : 'close', 'stop_at' : time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
+
+ def wkf_action_post(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state' : 'post'}, context=context)
+
+ def get_current_session(self, cr, uid, context=None):
+ current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+ domain = [
+ ('state', '=', 'open'),
+ ('start_at', '>=', time.strftime('%Y-%m-%d 00:00:00')),
+ ('config_id.user_id', '=', uid),
+ ]
+ session_ids = self.search(cr, uid, domain, context=context, limit=1, order='start_at desc')
+ session_id = session_ids[0] if session_ids else False
+
+ if not session_id:
+ pos_config_proxy = self.pool.get('pos.config')
+ pos_config_ids = pos_config_proxy.search(cr, uid, [('user_id', '=', uid),('state', '=', 'active')], limit=1, order='create_date desc')
+
+ if not pos_config_ids:
+ raise osv.except_osv(_('Error !'),
+ _('There is no active PoS Config for this User %s') % current_user.name)
+
+ config = pos_config_proxy.browse(cr, uid, pos_config_ids[0], context=context)
+
+ values = {
+ 'state' : 'draft',
+ 'start_at' : time.strftime('%Y-%m-%d %H:%M:%S'),
+ 'config_id' : config.id,
+ 'journal_id' : config.journal_id.id,
+ 'user_id': current_user.id,
+ }
+ session_id = self.create(cr, uid, values, context=context)
+ wkf_service = netsvc.LocalService('workflow')
+ wkf_service.trg_validate(uid, 'pos.session', session_id, 'open', cr)
+
+ return session_id
+
+pos_session()
+
class pos_config_journal(osv.osv):
""" Point of Sale journal configuration"""
_name = 'pos.config.journal'
@@ -54,6 +292,7 @@ class pos_order(osv.osv):
def create_from_ui(self, cr, uid, orders, context=None):
#_logger.info("orders: %r", orders)
list = []
+ session_id = self.pool.get('pos.session').get_current_session()
for order in orders:
# order :: {'name': 'Order 1329148448062', 'amount_paid': 9.42, 'lines': [[0, 0, {'discount': 0, 'price_unit': 1.46, 'product_id': 124, 'qty': 5}], [0, 0, {'discount': 0, 'price_unit': 0.53, 'product_id': 62, 'qty': 4}]], 'statement_ids': [[0, 0, {'journal_id': 7, 'amount': 9.42, 'name': '2012-02-13 15:54:12', 'account_id': 12, 'statement_id': 21}]], 'amount_tax': 0, 'amount_return': 0, 'amount_total': 9.42}
order_obj = self.pool.get('pos.order')
@@ -131,8 +370,7 @@ class pos_order(osv.osv):
return super(pos_order, self).copy(cr, uid, id, d, context=context)
_columns = {
- 'name': fields.char('Order Ref', size=64, required=True,
- states={'draft': [('readonly', False)]}, readonly=True),
+ 'name': fields.char('Order Ref', size=64, required=True, readonly=True),
'company_id':fields.many2one('res.company', 'Company', required=True, readonly=True),
'shop_id': fields.many2one('sale.shop', 'Shop', required=True,
states={'draft': [('readonly', False)]}, readonly=True),
@@ -147,6 +385,13 @@ class pos_order(osv.osv):
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, states={'draft': [('readonly', False)]}, readonly=True),
'partner_id': fields.many2one('res.partner', 'Customer', change_default=True, select=1, states={'draft': [('readonly', False)], 'paid': [('readonly', False)]}),
+ 'session_id' : fields.many2one('pos.session', 'Session',
+ #required=True,
+ select=1,
+ domain="[('state', '=', 'opened')]",
+ states={'draft' : [('readonly', False)]},
+ readonly=True),
+
'state': fields.selection([('draft', 'New'),
('cancel', 'Cancelled'),
('paid', 'Paid'),
@@ -172,7 +417,7 @@ class pos_order(osv.osv):
_defaults = {
'user_id': lambda self, cr, uid, context: uid,
'state': 'draft',
- 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'pos.order'),
+ 'name': '/',
'date_order': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'nb_print': 0,
'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
@@ -181,6 +426,10 @@ class pos_order(osv.osv):
'pricelist_id': _default_pricelist,
}
+ def create(self, cr, uid, values, context=None):
+ values['name'] = self.pool.get('ir.sequence').get(cr, uid, 'pos.order')
+ return super(pos_order, self).create(cr, uid, values, context=context)
+
def test_paid(self, cr, uid, ids, context=None):
"""A Point of Sale is paid when the sum
@return: True
@@ -248,7 +497,7 @@ class pos_order(osv.osv):
if not len(ids):
return False
for order in self.browse(cr, uid, ids, context=context):
- if order.state<>'cancel':
+ if order.state != 'cancel':
raise osv.except_osv(_('Error!'), _('In order to set to draft a sale, it must be cancelled.'))
self.write(cr, uid, ids, {'state': 'draft'})
wf_service = netsvc.LocalService("workflow")
@@ -442,7 +691,8 @@ class pos_order(osv.osv):
property_obj=self.pool.get('ir.property')
for order in self.browse(cr, uid, ids, context=context):
- if order.state<>'paid': continue
+ if order.state != 'paid':
+ continue
curr_c = res_obj.browse(cr, uid, uid).company_id
comp_id = res_obj.browse(cr, order.user_id.id, order.user_id.id).company_id
@@ -455,6 +705,7 @@ class pos_order(osv.osv):
# Create an entry for the sale
move_id = account_move_obj.create(cr, uid, {
+ 'ref' : order.name,
'journal_id': order.sale_journal.id,
}, context=context)
@@ -502,10 +753,9 @@ class pos_order(osv.osv):
if tax_code_id:
break
-
# Create a move for the line
account_move_line_obj.create(cr, uid, {
- 'name': line.name,
+ 'name': line.product_id.name,
'date': order.date_order[:10],
'ref': order.name,
'quantity': line.qty,
@@ -534,7 +784,7 @@ class pos_order(osv.osv):
continue
account_move_line_obj.create(cr, uid, {
- 'name': "Tax" + line.name,
+ 'name': "Tax" + line.name + " (%s)" % (tax.name),
'date': order.date_order[:10],
'ref': order.name,
'product_id':line.product_id.id,
@@ -573,7 +823,7 @@ class pos_order(osv.osv):
# counterpart
to_reconcile.append(account_move_line_obj.create(cr, uid, {
- 'name': order.name,
+ 'name': "Trade Receivables", #order.name,
'date': order.date_order[:10],
'ref': order.name,
'move_id': move_id,
@@ -587,6 +837,7 @@ class pos_order(osv.osv):
'period_id': period,
'partner_id': order.partner_id and order.partner_id.id or False
}, context=context))
+
self.write(cr, uid, order.id, {'state':'done', 'account_move': move_id}, context=context)
return True
@@ -594,8 +845,7 @@ class pos_order(osv.osv):
return self.write(cr, uid, ids, {'state': 'payment'}, context=context)
def action_paid(self, cr, uid, ids, context=None):
- context = context or {}
- self.create_picking(cr, uid, ids, context=None)
+ self.create_picking(cr, uid, ids, context=context)
self.write(cr, uid, ids, {'state': 'paid'}, context=context)
return True
diff --git a/addons/point_of_sale/point_of_sale_demo.xml b/addons/point_of_sale/point_of_sale_demo.xml
index 577d025c4fd..3a8b4a308c1 100644
--- a/addons/point_of_sale/point_of_sale_demo.xml
+++ b/addons/point_of_sale/point_of_sale_demo.xml
@@ -134,6 +134,7 @@
+
diff --git a/addons/point_of_sale/point_of_sale_sequence.xml b/addons/point_of_sale/point_of_sale_sequence.xml
index bf83fe17942..4bcab244c56 100644
--- a/addons/point_of_sale/point_of_sale_sequence.xml
+++ b/addons/point_of_sale/point_of_sale_sequence.xml
@@ -28,5 +28,17 @@
4
+
+ POS Session
+ pos.session
+
+
+
+ POS Session
+ pos.session
+ POS/%(day)s/(month)s/%(year)s/
+ 4
+
+
diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml
index 2622c2ba863..c8a666f4a52 100644
--- a/addons/point_of_sale/point_of_sale_view.xml
+++ b/addons/point_of_sale/point_of_sale_view.xml
@@ -18,6 +18,7 @@