5
# FINANCE UNIT TESTS TOOLS
6
# Developer: Vincent GREINER
9
from __future__ import print_function
10
from unifield_test import UnifieldTestException
11
from unifield_test import UnifieldTest
12
from datetime import datetime
13
from datetime import timedelta
14
from time import strftime
15
from time import sleep
16
from random import randint
17
from random import randrange
18
from oerplib import error
23
'register_line': "[%s] %s", # "[tag] uuid"
27
'cheque_number': "cheque %s",
28
# invoice line name: "[tag] (invoice id)/L(line number) (account)"
29
'invoice_line': "[%s] %d/L%03d %s",
32
AMOUNT_TOTAL_DIFF_DELTA = 0.01
34
CHECK_IS_CORRIGIBLE = False
37
class FinanceTestException(UnifieldTestException):
41
class FinanceTest(UnifieldTest):
43
def __init__(self, *args, **kwargs):
45
Include some finance data in the databases (except sync one).
46
Include/create them only if they have not been already created.
47
To know this, we use the key: "finance_test_class"
49
super(FinanceTest, self).__init__(*args, **kwargs)
51
def _hook_db_process(self, name, database):
53
Check that finance data are loaded into the given database
55
keyword = 'finance_test_class'
57
# If no one, do some changes on DBs
58
if not self.is_keyword_present(database, keyword):
59
# 00 Open periods from january to today's one
62
temporay bypass this dataset as done in cor testing
63
and since no other tests performed for finance area
65
month = strftime('%m')
66
today = strftime('%Y-%m-%d')
67
fy_obj = database.get('account.fiscalyear')
68
period_obj = database.get('account.period')
70
fy_ids = fy_obj.search([
71
('date_start', '<=', today),
72
('date_stop', '>=', today)
74
self.assert_(fy_ids != False, 'No fiscalyear found!')
76
# Sort periods by number
77
periods = period_obj.search([
78
('fiscalyear_id', 'in', fy_ids),
79
('number', '<=', month),
80
('state', '=', 'created')
82
for period in periods:
84
period_obj.action_set_state(period, {'state': 'draft'})
85
except error.RPCError as e:
86
print(e.oerp_traceback)
89
raise Exception('error', str(e))
91
# Write the fact that data have been loaded
92
database.get(self.test_module_obj_name).create({
96
print (database.colored_name + ' [' + colors.BGreen + \
97
'OK'.center(4) + colors.Color_Off + '] %s: Data loaded' % (
100
print (database.colored_name + ' [' + colors.BYellow + \
101
'WARN'.center(4) + colors.Color_Off \
102
+ '] %s: Data already exists' % (keyword, ))
103
return super(FinanceTest, self)._hook_db_process(name, database)
105
def get_journal_ids(self, db, journal_type, is_of_instance=False,
107
model = 'account.analytic.journal' if is_analytic \
108
else 'account.journal'
109
domain = [('type', '=', journal_type)]
111
domain.append(('is_current_instance', '=', True))
113
ids = db.get(model).search(domain)
116
"no %s journal(s) found" % (journal_type, )
120
def get_account_from_code(self, db, code, is_analytic=False):
121
model = 'account.analytic.account' if is_analytic \
122
else 'account.account'
123
ids = db.get(model).search([('code', '=', code)])
124
return ids and ids[0] or False
126
def get_account_code(self, db, id, is_analytic=False):
127
model = 'account.analytic.account' if is_analytic \
128
else 'account.account'
129
return db.get(model).browse(id).code
131
def get_random_amount(self, is_expense=False):
132
amount = float(randrange(100, 10000))
137
def journal_create(self, db, name, code, journal_type,
138
analytic_journal_id=False, account_code=False, currency_name=False,
139
bank_journal_id=False):
142
if of type bank/cash/cheque: account_code and currency_name needed.
144
:type db: oerplib object
145
:param name: journal name
146
:param code: journal code
147
:param journal_type: journal type. available types::
169
:param analytic_journal_id: (optional) linked analytic journal id
170
default attempt to search an analytic journal that have the same
172
:param account_code: (mandatory for bank/cash/cheque) account
173
code that will be used in debit/credit for the journal
174
:param currency_name: (mandatory for bank/cash/cheque) journal
176
:param bank_journal_id: (mandatory for cheque) linked bank journal
182
name and code and journal_type,
183
"name or/and code or/and journal type missing"
186
if journal_type in ('bank', 'cheque', 'cash', ):
188
account_code and currency_name,
189
"bank/cash/cheque: account code and currency required." \
190
" account: '%s', currency: '%s'" % (
191
account_code or '', currency_name or '', )
194
if journal_type == 'cheque':
196
bank_journal_id != False,
197
"bank journal mandatory for cheque journal"
200
aaj_obj = db.get('account.analytic.journal')
201
aa_obj = db.get('account.account')
202
ccy_obj = db.get('res.currency')
203
aj_obj = db.get('account.journal')
206
if not analytic_journal_id:
207
analytic_journal_type = journal_type
208
if journal_type in ('bank', 'cheque', ):
209
analytic_journal_type = 'cash'
210
aaj_ids = aaj_obj.search([('type', '=', analytic_journal_type)])
213
"no analytic journal found with this type: %s" % (
216
analytic_journal_id = aaj_ids[0]
222
'type': journal_type,
223
'analytic_journal_id': analytic_journal_id,
226
a_ids = aa_obj.search([('code', '=', account_code)])
229
"no account found for the given code: %s" % (account_code, )
231
account_id = a_ids[0]
233
'default_debit_account_id': account_id,
234
'default_credit_account_id': account_id,
237
c_ids = ccy_obj.search([('name', '=', currency_name)])
240
"currency not found: %s" % (currency_name, )
242
vals.update({'currency': c_ids[0]})
244
vals['bank_journal_id'] = bank_journal_id
246
return aj_obj.create(vals)
248
def register_create(self, db, name, code, register_type, account_code,
249
currency_name, bank_journal_id=False):
251
create a register in the current period.
254
:type db: oerplib object
255
:param name: register name (used as journal's name)
256
:param code: register's code (used as journal's code)
257
:param register_type: register available types::
261
:param account_code: account code used for debit/credit account
262
at journal creation. (so used by the register)
263
:param currency_name: name of currency to use(must exists)
264
:param bank_journal_id: (mandatory for cheque) linked bank journal
265
:return: register_id and journal_id
266
:rtype: tuple (registed id, journal id)
268
aaj_obj = db.get('account.analytic.journal')
269
abs_obj = db.get('account.bank.statement')
271
analytic_journal_code_map = {
276
aaj_code = analytic_journal_code_map[register_type]
277
aaj_ids = aaj_obj.search([('code', '=', aaj_code)])
280
"analytic journal code %s not found" % (aaj_code, )
283
j_id = self.journal_create(db, name, code, register_type,
284
account_code=account_code, currency_name=currency_name,
285
bank_journal_id=bank_journal_id,
286
analytic_journal_id=aaj_ids[0])
287
# search the register (should be created by journal creation)
288
reg_ids = abs_obj.search([('journal_id', '=', j_id)])
289
return reg_ids and reg_ids[0] or False, j_id
291
def register_create_line(self, db, regbr_or_id, account_code_or_id, amount,
292
ad_breakdown_data=False,
293
date=False, document_date=False,
294
third_partner_id=False, third_employee_id=False,
295
third_journal_id=False,
296
do_temp_post=False, do_hard_post=False,
299
create a register line in the given register
301
:type db: oerplib object
302
:param regbr_or_id: parent register browsed object or id
303
:type regbr_or_id: object/int/long
304
:param account_code_or_id: account code to search or account_id
305
:type code_or_id: str/int/long
306
:param amount: > 0 amount IN, < 0 amount OUT
307
:param ad_breakdown_data: (optional) see analytic_distribution_create
308
breakdown_data param help
309
:param datetime date: posting date
310
:param datetime document_date: document date
311
:param third_partner_id: partner id
312
:param third_employee_id: emp id (operational advance)
313
:param third_journal_id: journal id (internal transfer)
314
:return: register line id and AD id and target ji for correction
315
(if hard host or temp post)
316
:rtype: tuple (register_line_id, ad_id/False, ji_id)
319
self.assert_(regbr_or_id != False, "register missing")
321
not (do_hard_post and do_temp_post) ,
322
"you can not temp post and hard post at the same time"
325
abs_obj = db.get('account.bank.statement')
326
absl_obj = db.get('account.bank.statement.line')
327
aa_obj = db.get('account.account')
329
if isinstance(regbr_or_id, (int, long)):
330
register_br = abs_obj.browse(regbr_or_id)
332
register_br = regbr_or_id
335
if isinstance(account_code_or_id, (str, unicode)):
337
code_ids = aa_obj.search([
339
('name', 'ilike', account_code_or_id),
340
('code', 'ilike', account_code_or_id)]
344
"error searching for this account code: %s" % (
345
account_code_or_id, )
349
"error more than 1 account with code: %s" % (
350
account_code_or_id, )
352
account_id = code_ids[0]
354
account_id = account_code_or_id
355
account_br = aa_obj.browse(account_id)
359
date_start = register_br.period_id.date_start or False
360
date_stop = register_br.period_id.date_stop or False
362
date_start and date_stop,
363
"no date found for the period %s" % (
364
register_br.period_id.name, )
366
random_date = self.random_date(
367
datetime.strptime(str(date_start), '%Y-%m-%d'),
368
datetime.strptime(str(date_stop), '%Y-%m-%d')
370
date = datetime.strftime(random_date, '%Y-%m-%d')
371
if not document_date:
375
name = FINANCE_TEST_MASK['register_line'] % (tag, self.get_uuid(), )
377
'statement_id': register_br.id,
378
'account_id': account_id,
379
'document_date': document_date,
385
vals['partner_id'] = third_partner_id
386
if third_employee_id:
387
vals['employee_id'] = third_employee_id
389
vals['transfer_journal_id'] = third_journal_id
390
if register_br.journal_id.type == 'cheque':
391
vals['cheque_number'] = FINANCE_TEST_MASK['cheque_number'] % (
392
self.proxy.get_uuid(), )
395
regl_id = absl_obj.create(vals)
398
if ad_breakdown_data and account_br.is_analytic_addicted:
399
distrib_id = self.analytic_distribution_create(db,
400
breakdown_data=ad_breakdown_data)
401
absl_obj.write([regl_id],
402
{'analytic_distribution_id': distrib_id}, {'fake': 1, })
408
self.register_line_temp_post(db, regl_id)
410
self.register_line_hard_post(db, regl_id)
411
if do_temp_post or do_hard_post:
412
target_ji_id = self.register_line_get_target_ji(db, regl_id,
413
account_br.code, account_br.is_analytic_addicted)
415
return (regl_id, distrib_id, target_ji_id, )
417
def register_line_temp_post(self, db, regl_id):
418
if isinstance(regl_id, (int, long, )):
420
db.get('account.bank.statement.line').button_temp_posting(regl_id,
423
def register_line_hard_post(self, db, regl_id):
424
if isinstance(regl_id, (int, long, )):
426
db.get('account.bank.statement.line').button_hard_posting(regl_id,
429
def register_line_get_target_ji(self, db, regl_id, account_code,
432
return regline related target ji for corrections
433
(the reg line need to be temp or hard posted: journal items here)
435
absl_obj = db.get('account.bank.statement.line')
436
aml_obj = db.get('account.move.line')
439
('name', '=', absl_obj.browse(regl_id).name),
440
('account_id.code', '=', account_code),
442
if is_expense_account:
443
domain.append(('account_id.is_analytic_addicted', '=', 'True'))
445
aml_ids = aml_obj.search(domain)
446
return aml_ids and aml_ids[0] or False
448
def register_close(self, db, ids):
449
if isinstance(ids, (int, long, )):
451
abs_obj = db.get('account.bank.statement')
452
wcb_obj = db.get('wizard.confirm.bank')
454
for abs_br in abs_obj.browse(ids):
455
abs_ids = [abs_br.id]
456
# set fake balance amount...
457
abs_obj.write(abs_ids, {
458
'balance_end_real': abs_br.balance_end,
460
abs_obj.button_confirm_closing_bank_balance(abs_ids)
463
abs_obj.write(abs_ids, {
465
'closing_date': self.get_orm_date_now(),
468
def register_reopen(self, db, ids):
469
if isinstance(ids, (int, long, )):
471
db.get('account.bank.statement').write(ids, {
473
'closing_date': False,
476
def analytic_distribution_set_fp_account_dest(self, db,
477
fp_name, account_code, dest_code):
479
add account/dest tuple to FP
481
if fp_name and fp_name == 'PF':
482
return # nothing to do
484
fp_name and account_code and dest_code,
485
"you must give fp name and account/dest codes"
488
fp_id = self.get_account_from_code(db, fp_name, is_analytic=True)
491
"FP '%s' not found" % (fp_name, )
493
account_id = self.get_account_from_code(db, account_code,
497
"account '%s' not found" % (account_code, )
499
dest_id = self.get_account_from_code(db, dest_code, is_analytic=True)
502
"dest '%s' not found" % (dest_code, )
505
aaa_obj = db.get('account.analytic.account')
506
fp_br = aaa_obj.browse(fp_id)
508
# search account/dest tuple in FP ?
509
for tda in fp_br.tuple_destination_account_ids:
510
if tda.account_id.id == account_id and \
511
tda.destination_id.id == dest_id:
512
return # account/dest tuple already in FP
514
tuple_dest_obj = db.get('account.destination.link')
515
tuple_id = tuple_dest_obj.search([('account_id', '=', account_id) , ('destination_id', '=', dest_id)])
517
tuple_id = tuple_dest_obj.create({'account_id': account_id, 'destination_id': dest_id})
519
tuple_id = tuple_id[0]
520
aaa_obj.write(fp_id, {'tuple_destination_account_ids': [(4, tuple_id)]})
522
def analytic_distribution_create(self, db,
523
breakdown_data=[(100., 'OPS', False, False)]):
525
create analytic distribution
527
:type db: oerplib object
528
:param account_id: related account._id (if not set search for a random
530
:type account_id: int
531
:param breakdown_data: [(purcent, dest, cc, fp)]
532
- for breakdown of lines list: percent, dest code, cc code, fp code
533
- False cc for default company top cost center
537
comp_obj = db.get('res.company')
538
aaa_obj = db.get('account.analytic.account')
539
ad_obj = db.get('analytic.distribution')
541
company = comp_obj.browse(comp_obj.search([])[0])
542
funding_pool_pf_id = self.get_record_id_from_xmlid(db,
543
'analytic_distribution', 'analytic_account_msf_private_funds', )
544
self.assert_(funding_pool_pf_id != False, 'PF funding pool not found')
547
if not breakdown_data:
548
breakdown_data = [(100., 'OPS', 'HT101', 'PF', ), ]
551
name = FINANCE_TEST_MASK['ad'] % (self.get_uuid(), )
552
distrib_id = ad_obj.create({'name': name})
554
for purcent, dest, cc, fp in breakdown_data:
555
dest_id = aaa_obj.search([
556
('category', '=', 'DEST'),
557
('type', '=', 'normal'),
562
'no destination found %s' % (dest, )
567
cost_center_id = aaa_obj.search([
568
('category', '=', 'OC'),
569
('type', '=', 'normal'),
573
cost_center_id != False,
574
'no cost center found %s' % (cc, )
576
cost_center_id = cost_center_id[0]
578
cost_center_id = company.instance_id.top_cost_center_id \
579
and company.instance_id.top_cost_center_id.id or False
581
cost_center_id != False,
582
'no top cost center found for instance %s' % (
583
company.name or '', )
586
funding_pool_id = aaa_obj.search([
587
('category', '=', 'FUNDING'),
588
('type', '=', 'normal'),
592
funding_pool_id != False,
593
'no funding pool found %s' % (fp, )
595
funding_pool_id = funding_pool_id[0]
597
funding_pool_id = funding_pool_pf_id # default PF
599
# relating ad line dimension distribution lines (1 cc, 1fp)
601
('cost.center.distribution.line', cost_center_id, False),
602
('funding.pool.distribution.line', funding_pool_id,
605
for ad_dim_analytic_obj, val, fpdim_cc_id in data:
607
'distribution_id': distrib_id,
610
'cost_center_id': fpdim_cc_id,
611
'percentage': purcent,
612
'currency_id': company.currency_id.id,
613
'destination_id': dest_id,
615
db.get(ad_dim_analytic_obj).create(vals)
618
def simulation_correction_wizard(self,
622
new_account_code=False,
623
new_ad_breakdown_data=False,
624
ad_replace_data=False):
626
:param new_account_code: new account code for a G/L correction
627
:param new_ad_breakdown_data: new ad lines to replace all ones (delete)
628
:param ad_replace_data: { percent_key: {'dest/cc/fp/per': new_val, }
629
ad_replace_data: percent_key to identify the line where to replace
631
ad_replace_data={ 100.: {'dest': new_dest, } },
636
choose between delete and recreate AD with new_ad_breakdown_data
637
or to replace dest/cc/fp/percentage values with ad_replace_data
639
wizard_cor_obj = db.get('wizard.journal.items.corrections')
640
wizard_corl_obj = db.get('wizard.journal.items.corrections.lines')
641
wizard_ad_obj = db.get('analytic.distribution.wizard')
642
wizard_adl_obj = db.get('analytic.distribution.wizard.lines')
643
wizard_adfpl_obj = db.get('analytic.distribution.wizard.fp.lines')
645
aa_obj = db.get('account.account')
646
aml_obj = db.get('account.move.line')
647
aaa_obj = db.get('account.analytic.account')
649
# check valid correction
651
new_account_code or new_ad_breakdown_data or ad_replace_data,
652
'no correction changes: required G/L or AD or both'
654
# check valid correction
656
not (new_ad_breakdown_data and ad_replace_data),
657
'you can not both redefine full AD and replace attributes'
660
# get new account id for a G/L correction
661
new_account_id = False
663
account_ids = aa_obj.search([('code', '=', new_account_code)])
665
account_ids != False,
666
'account %s for a G/L correction not found' % (
669
new_account_id = account_ids[0]
672
ji_br = aml_obj.browse(ji_to_correct_id)
675
'journal item not found'
679
ji_br.account_id.code != new_account_code,
680
'you can not do a G/L correction with same account code'
682
if CHECK_IS_CORRIGIBLE:
685
"JI item '%s' is not corrigible (and should be)" % (
688
old_account_id = ji_br.account_id and ji_br.account_id.id or False
689
ji_amount = ji_br.debit_currency and ji_br.debit_currency * -1 or \
690
ji_br.credit_currency
693
cor_date = ji_br.date
694
if not isinstance(cor_date, str):
695
cor_date = self.date2orm(cor_date)
697
# set wizard header (will generate in create the correction lines)
700
'move_line_id': ji_to_correct_id,
702
'from_donation': False,
704
wiz_br = wizard_cor_obj.browse(wizard_cor_obj.create(vals))
706
# set the generated correction line
707
wiz_cor_line = wiz_br.to_be_corrected_ids.next()
709
wiz_cor_line != False,
710
'error generating a correction line'
714
if new_account_id: # G/L correction
715
wizard_corl_obj.write([wiz_cor_line.id],
716
{'account_id' : new_account_id})
718
if new_ad_breakdown_data or ad_replace_data:
720
action = wizard_corl_obj.button_analytic_distribution(
721
[wiz_cor_line.id], {'fake': 1})
723
wizard_ad_id = action['res_id'][0]
724
wizard_ad_br = wizard_ad_obj.browse(wizard_ad_id)
726
wizard_ad_br != False,
727
"error getting AD wizard record from action: %s" % (
740
if wizard_ad_br.line_ids:
741
# CC lines: 'cost_center_id' False, 'destination_id' dest,
742
# 'analytic_id' <=> CC
743
# as rpc browse failed here: dirty workaround with read
744
line_ids = [ l.id for l in wizard_ad_br.line_ids ]
745
for adwl_r in wizard_adl_obj.read(line_ids, fields):
746
percent = adwl_r['percentage']
748
if percent in ad_replace_data:
749
# line is target of replace
751
replace_vals = ad_replace_data[percent]
753
# destination replace
754
if 'dest' in replace_vals:
755
ad_line_vals['destination_id'] = \
756
self.get_account_from_code(db,
757
replace_vals['dest'],
760
# cost center replace
761
if 'cc' in replace_vals:
762
ad_line_vals['analytic_id'] = \
763
self.get_account_from_code(db,
768
if 'per' in replace_vals:
769
percent = replace_vals['per']
771
if ad_line_vals or 'per' in replace_vals:
772
# (always needed percentage in vals)
773
ad_line_vals['percentage'] = percent
774
ad_line_vals['is_percentage_amount_touched'] = True
775
wizard_adl_obj.write([adwl_r['id']],
778
# supply update amount from cc lines
779
# NOTE: for finance (state != 'cc') the amount is to be
780
# computed from amount of fp lines
781
if line_ids and wizard_ad_br.state != 'cc':
783
in wizard_adl_obj.read(line_ids, ['amount']):
784
total_amount += adwl_r['amount']
786
if wizard_ad_br.fp_line_ids:
787
# FP LINES: 'cost_center_id', 'destination_id',
788
# 'analytic_id' <=> FP
789
# as rpc browse failed here: dirty workaround with read
790
fp_line_ids = [ l.id for l in wizard_ad_br.fp_line_ids ]
791
for adwl_r in wizard_adfpl_obj.read(fp_line_ids, fields):
792
percent = adwl_r['percentage']
794
if percent in ad_replace_data:
795
# line is target of replace
797
replace_vals = ad_replace_data[percent]
799
# destination replace
800
if 'dest' in replace_vals:
801
ad_line_vals['destination_id'] = \
802
self.get_account_from_code(db,
803
replace_vals['dest'],
806
# cost center replace
807
if 'cc' in replace_vals:
808
ad_line_vals['cost_center_id'] = \
809
self.get_account_from_code(db,
814
if 'fp' in replace_vals:
815
ad_line_vals['analytic_id'] = \
816
self.get_account_from_code(db,
821
if 'per' in replace_vals:
822
percent = replace_vals['per']
824
if ad_line_vals or 'per' in replace_vals:
825
# (always needed percentage in vals)
826
ad_line_vals['percentage'] = percent
827
ad_line_vals['is_percentage_amount_touched'] = True
828
wizard_adfpl_obj.write([adwl_r['id']],
831
# finance update amount from fp lines
832
# NOTE: for supply (state == 'cc') the amount is to be
833
# computed from amount of cc lines
834
if fp_line_ids and wizard_ad_br.state != 'cc':
835
for adwl_r in wizard_adfpl_obj.read(fp_line_ids,
837
total_amount += adwl_r['amount']
839
elif new_ad_breakdown_data:
844
if wizard_ad_br.line_ids:
846
del_vals['line_ids'] = [ (2, l.id, ) \
847
for l in wizard_ad_br.line_ids ]
848
if wizard_ad_br.fp_line_ids:
850
del_vals['fp_line_ids'] = [ (2, l.id, ) \
851
for l in wizard_ad_br.fp_line_ids ]
853
wizard_ad_obj.write([wizard_ad_id], del_vals)"""
854
todel_ids = wizard_adl_obj.search(
855
[('wizard_id', '=', wizard_ad_br.id)])
857
wizard_adl_obj.unlink(todel_ids)
858
todel_ids = wizard_adfpl_obj.search(
859
[('wizard_id', '=', wizard_ad_br.id)])
861
wizard_adfpl_obj.unlink(todel_ids)
864
ccy_id = self.get_company(db).currency_id.id
867
for percent, dest, cc, fp in new_ad_breakdown_data:
868
dest_id = self.get_account_from_code(db, dest,
870
cc_id = self.get_account_from_code(db, cc,
872
fp_id = self.get_account_from_code(db, fp,
875
total_amount += (ji_amount * percent) / 100.
878
# 'destination_id' dest, # 'analytic_id' <=> CC
879
# not set amount here as will be auto computed from percent
880
ad_line_vals.append({
881
'wizard_id': wizard_ad_br.id,
882
'analytic_id': cc_id,
883
'percentage': percent,
884
'currency_id': ccy_id,
885
'destination_id': dest_id,
886
'is_percentage_amount_touched': True,
890
# FP LINES: 'cost_center_id', 'destination_id',
891
# 'analytic_id' <=> FP
892
# not set amount here as will be auto computed from percent
893
ad_fp_line_vals.append({
894
'wizard_id': wizard_ad_br.id,
895
'analytic_id': fp_id,
896
'percentage': percent,
897
'currency_id': ccy_id,
898
'destination_id': dest_id,
899
'cost_center_id': cc_id,
900
'is_percentage_amount_touched': True,
903
if ad_line_vals and ad_fp_line_vals:
904
for new_vals in ad_line_vals:
905
wizard_adl_obj.create(new_vals)
906
for new_vals in ad_fp_line_vals:
907
wizard_adfpl_obj.create(new_vals)
908
# end new ad breakdown
910
# will validate cor wizard too
911
if total_amount and (ad_replace_data or new_ad_breakdown_data):
912
# set wizard header vals to update
914
'amount': total_amount,
915
'state': 'correction',
918
# needed for AD wizard to process directly from it a G/L
921
'old_account_id': old_account_id,
922
'account_id': new_account_id,
924
wizard_ad_obj.write([wizard_ad_id], ad_wiz_vals)
926
# confirm the wizard with adoc context values to process a
929
'from': 'wizard.journal.items.corrections',
933
wizard_ad_obj.button_confirm([wizard_ad_id], context)
934
return # G/L account change already processed line above
937
# G/L correction without AD correction: confirm wizard
938
# (with an AD correction, cor is confirmed by AD wizard)
939
# action_confirm(ids, context=None, distrib_id=False)
940
wizard_cor_obj.action_confirm([wiz_br.id], {}, False)
942
def check_sequence_number(self, model_obj, entries_ids, is_analytic=False,
945
check sequence number consistency
946
:param model_obj: oerplib model object
947
:param entries_ids: entries ids
948
:type entries_ids: int/long/list
949
:param analytic: is analytic entry ?
951
:param db_name: database name
954
def get_entry_sequence(entry_br):
955
return is_analytic and entry_br.entry_sequence \
956
or entry_br.move_id.name
958
if isinstance(entries_ids, (int, long, )):
959
entries_ids = [ entries_ids ]
961
entries_seq_number = [ get_entry_sequence(e_br) for e_br \
962
in model_obj.browse(entries_ids) ]
964
assert_pattern_header = db_name \
965
and "sequence number mismatch :: %s: %s" \
966
or "sequence number mismatch: %s"
968
len(set(entries_seq_number)) == 1,
969
assert_pattern_header % (db_name, ', '.join(entries_seq_number)),
973
def check_ji_correction(self, db, ji_id,
974
account_code, new_account_code=False,
975
expected_ad=False, expected_ad_rev=False, expected_ad_cor=False,
976
expected_cor_rev_ajis_total_func_amount=False,
977
cor_level=1, ji_origin_id=False,
978
check_sequence_number=False):
980
ji_origin_id: 1st ji corrected for cor cascade
981
cor_level: cor level for cor cascade
983
def get_rev_cor_amount_and_field(base_amount, is_rev):
986
amount_field = 'credit_currency' \
987
if base_amount > 0 else 'debit_currency'
988
return (base_amount, amount_field, )
990
aml_obj = db.get('account.move.line')
991
aal_obj = db.get('account.analytic.line')
993
od_journal_ids = self.get_journal_ids(db, 'correction',
994
is_of_instance=False, is_analytic=False)
995
aod_journal_ids = self.get_journal_ids(db, 'correction',
996
is_of_instance=False, is_analytic=True)
998
account_id = self.get_account_from_code(db, account_code,
1000
new_account_id = False
1001
if new_account_code:
1002
new_account_id = self.get_account_from_code(db, new_account_code,
1005
new_account_id != False,
1006
"new account '%s' not found" % (new_account_id, )
1009
ji_br = aml_obj.browse(ji_id)
1010
ji_seq_num = ji_br.move_id.name
1011
ji_origin = ji_origin_id and aml_obj.browse(ji_origin_id).name or \
1013
ji_amount = ji_br.debit_currency and ji_br.debit_currency * -1 or \
1014
ji_br.credit_currency
1016
if new_account_code and new_account_code != account_code:
1020
cor_rev_amount, cor_rev_amount_field = get_rev_cor_amount_and_field(
1023
('journal_id', 'in', od_journal_ids),
1024
('reversal_line_id', '=', ji_id),
1025
('account_id', '=', account_id),
1026
(cor_rev_amount_field, '=', abs(cor_rev_amount)),
1028
rev_ids = aml_obj.search(domain)
1031
"no JI REV found for %s %s %f:: %s" % (account_code,
1032
ji_br.name, ji_amount, db.colored_name, )
1034
if check_sequence_number:
1035
# check sequence number
1036
self.check_sequence_number(aml_obj, rev_ids, is_analytic=False,
1037
db_name=db.colored_name)
1040
cor_rev_amount, cor_rev_amount_field = get_rev_cor_amount_and_field(
1043
('journal_id', 'in', od_journal_ids),
1044
('corrected_line_id', '=', ji_id),
1045
('account_id', '=', new_account_id),
1046
(cor_rev_amount_field, '=', abs(cor_rev_amount)),
1048
cor_ids = aml_obj.search(domain)
1051
"no JI COR found for %s %s %f:: %s" % (new_account_code,
1052
ji_br.name, ji_amount, db.colored_name, )
1054
if check_sequence_number:
1055
# check sequence number
1056
self.check_sequence_number(aml_obj, cor_ids, is_analytic=False,
1057
db_name=db.colored_name)
1059
# ids of AJIs not rev/cor (not in correction journal)
1060
base_aji_ids = aal_obj.search([
1061
('move_id', '=', ji_id),
1062
('journal_id', 'not in', aod_journal_ids),
1063
('general_account_id', '=', account_id),
1065
# FIXME way of truely getting AJIs when cor of cor
1067
# FIXME remove and not cor_level > 1
1068
if expected_ad and not cor_level > 1:
1071
len(base_aji_ids) == len(expected_ad),
1072
"expected AJIs count do not match for JI %s %s %f:: %s" % (
1073
new_account_code or account_code,
1074
ji_br.name, ji_amount, db.colored_name, )
1078
for aal_br in aal_obj.browse(base_aji_ids):
1079
for percent, dest, cc, fp in expected_ad:
1080
if aal_br.general_account_id.id == account_id and \
1081
aal_br.destination_id.code == dest and \
1082
aal_br.cost_center_id.code == cc and \
1083
aal_br.account_id.code == fp and \
1084
aal_br.amount_currency == ((ji_amount*percent) / 100.):
1085
# check with percent match
1090
len(base_aji_ids) == match_count,
1091
"expected AJIs do not match for JI %s %f:: %s" % (
1092
ji_br.name, ji_amount, db.colored_name, )
1098
('journal_id', 'in', aod_journal_ids),
1099
('general_account_id', '=', account_id),
1102
domain.append(('reversal_origin', 'in', base_aji_ids))
1104
domain.append(('name', '=', "REV - %s" % (ji_br.name, )))
1105
ids = aal_obj.search(domain)
1107
len(ids) == len(expected_ad_rev),
1108
"expected REV AJIs count do not match for JI %s %s %f:: %s" % (
1109
new_account_code or account_code,
1110
ji_br.name, ji_amount, db.colored_name, )
1114
total_func_amount = 0
1115
for aal_br in aal_obj.browse(ids):
1116
total_func_amount += aal_br.amount
1117
for percent, dest, cc, fp in expected_ad_rev:
1118
if aal_br.general_account_id.id == account_id and \
1119
aal_br.destination_id.code == dest and \
1120
aal_br.cost_center_id.code == cc and \
1121
aal_br.account_id.code == fp and \
1122
aal_br.amount_currency == \
1123
(((ji_amount * percent) / 100.) * -1):
1128
len(ids) == match_count,
1129
"expected REV AJIs do not match for JI %s %s %f:: %s" % (
1130
new_account_code or account_code,
1131
ji_br.name, ji_amount, db.colored_name, )
1133
if expected_cor_rev_ajis_total_func_amount:
1134
amount_diff = abs(expected_cor_rev_ajis_total_func_amount) \
1135
- abs(total_func_amount)
1137
0 <= amount_diff <= AMOUNT_TOTAL_DIFF_DELTA,
1138
"expected REV AJIs total func amount %f not found:: %s" % (
1139
expected_cor_rev_ajis_total_func_amount,
1142
if check_sequence_number:
1143
# check sequence number
1144
self.check_sequence_number(aal_obj, ids, is_analytic=True,
1145
db_name=db.colored_name)
1149
ids = aal_obj.search([
1150
#('last_corrected_id', 'in', base_aji_ids),
1151
('journal_id', 'in', aod_journal_ids),
1152
('general_account_id', '=', new_account_id or account_id),
1153
('name', '=', "COR%d - %s" % (cor_level, ji_origin, )),
1156
len(ids) == len(expected_ad_cor),
1157
"expected COR AJIs count do not match for JI %s %s %f:: %s" % (
1158
new_account_code or account_code,
1159
ji_br.name, ji_amount, db.colored_name, )
1163
total_func_amount = 0
1164
for aal_br in aal_obj.browse(ids):
1165
total_func_amount += aal_br.amount
1166
for percent, dest, cc, fp in expected_ad_cor:
1167
# COR with new account
1168
gl_account_id = new_account_id or account_id
1169
if aal_br.general_account_id.id == gl_account_id and \
1170
aal_br.destination_id.code == dest and \
1171
aal_br.cost_center_id.code == cc and \
1172
aal_br.account_id.code == fp and \
1173
aal_br.amount_currency == \
1174
((ji_amount * percent) / 100.): # percent match ?
1179
len(ids) == match_count,
1180
"expected COR AJIs do not match for JI %s %s %f:: %s" % (
1181
new_account_code or account_code,
1182
ji_br.name, ji_amount, db.colored_name, )
1184
if expected_cor_rev_ajis_total_func_amount:
1185
amount_diff = abs(expected_cor_rev_ajis_total_func_amount) \
1186
- abs(total_func_amount)
1188
FIXME: diff of AMOUNT_TOTAL_DIFF_DELTA raises the assert
1190
0 <= amount_diff <= AMOUNT_TOTAL_DIFF_DELTA,
1191
"expected COR AJIs total func amount %f not found:: %s" % (
1192
expected_cor_rev_ajis_total_func_amount,
1195
if check_sequence_number:
1196
# check sequence number
1197
self.check_sequence_number(aal_obj, ids, is_analytic=True,
1198
db_name=db.colored_name)
1200
def journal_create_entry(self, database):
1202
Create a journal entry (account.move) with 2 lines:
1203
- an expense one (with an analytic distribution)
1205
Return the move ID, expense line ID, then counterpart ID
1207
# Prepare some values
1208
move_obj = database.get('account.move')
1209
aml_obj = database.get('account.move.line')
1210
period_obj = database.get('account.period')
1211
journal_obj = database.get('account.journal')
1212
partner_obj = database.get('res.partner')
1213
account_obj = database.get('account.account')
1214
distrib_obj = database.get('analytic.distribution')
1215
curr_date = strftime('%Y-%m-%d')
1217
journal_ids = journal_obj.search([('type', '=', 'purchase')])
1218
self.assert_(journal_ids != [], "No purchase journal found!")
1220
period_ids = period_obj.get_period_from_date(curr_date)
1222
partner_ids = partner_obj.search([('partner_type', '=', 'external')])
1223
# Create a random amount
1224
random_amount = randint(100, 10000)
1227
'journal_id': journal_ids[0],
1228
'period_id': period_ids[0],
1230
'document_date': curr_date,
1231
'partner_id': partner_ids[0],
1234
move_id = move_obj.create(move_vals)
1237
"Move creation failed with these values: %s" % move_vals
1239
# Create some move lines
1240
account_ids = account_obj.search([
1241
('is_analytic_addicted', '=', True),
1242
('code', '=', '6101-expense-test')]
1244
random_account = randint(0, len(account_ids) - 1)
1247
'account_id': account_ids[random_account],
1248
'name': 'fp_changes expense',
1249
'amount_currency': random_amount,
1251
# Search analytic distribution
1252
distribution_ids = distrib_obj.search([('name', '=', 'DISTRIB 1')])
1253
distribution_id = distrib_obj.copy(distribution_ids[0],
1254
{'name': 'distribution-test'})
1255
vals.update({'analytic_distribution_id': distribution_id})
1256
aml_expense_id = aml_obj.create(vals)
1257
counterpart_ids = account_obj.search([
1258
('is_analytic_addicted', '=', False),
1259
('code', '=', '401-supplier-test'), ('type', '!=', 'view')])
1260
random_counterpart = randint(0, len(counterpart_ids) - 1)
1262
'account_id': counterpart_ids[random_counterpart],
1263
'amount_currency': -1 * random_amount,
1264
'name': 'fp_changes counterpart',
1265
'analytic_distribution_id': False,
1267
aml_counterpart_id = aml_obj.create(vals)
1268
# Validate the journal entry
1269
# WARNING: we use button_validate so that it check the analytic
1270
# distribution validity/presence
1271
move_obj.button_validate([move_id])
1272
return move_id, aml_expense_id, aml_counterpart_id
1274
def get_period_id(self, db, month, year=0):
1275
year = datetime.now().year if year == 0 else year
1276
period_obj = db.get('account.period')
1278
ids = period_obj.search([
1279
('date_start', '=', "%04d-%02d-01" % (year, month, )),
1281
return ids and ids[0] or False
1283
def period_close(self, db, level, month, year=0):
1285
:param level: 'f', 'm' or 'h' for 'field', 'mission' or 'hq'
1287
self._period_close_reopen(db, level, month, year=year, reopen=False)
1289
def period_reopen(self, db, level, month, year=0):
1291
:param level: 'f', 'm' or 'h' for 'field', 'mission' or 'hq'
1293
self._period_close_reopen(db, level, month, year=year, reopen=True)
1295
def _period_close_reopen(self, db, level, month, year=0, reopen=None):
1297
close/reopen period at given level
1298
:param level: 'f', 'm' or 'h' for 'field', 'mission' or 'hq'
1301
level in ('f', 'm', 'h', ),
1302
"invalid level parameter 'f', 'm' or 'h' expected"
1306
reopen is not None and isinstance(reopen, bool),
1307
"invalid reopen parameter: bool expected"
1310
period_id = self.get_period_id(db, month, year=year)
1313
"period %02d/%04d not found" % (year, month, )
1316
period_obj = db.get('account.period')
1317
period_br = period_obj.browse(period_id)
1319
# check current state
1320
state = period_br.state
1323
if state == 'draft':
1324
return # already open
1326
if state in ('draft', 'field-closed', ):
1327
return # already mission reopen
1329
if state in ('draft', 'field-closed', 'mission-closed', ):
1330
return # already hq reopen
1333
if state in ('field-closed', 'mission-closed', 'done', ):
1334
return # already field closed
1336
if state in ('mission-closed', 'done', ):
1337
return # already mission closed
1340
return # already hq closed
1342
# reopen/close related registers
1343
abs_obj = db.get('account.bank.statement')
1344
reg_ids = abs_obj.search([
1345
('state', '!=', 'open' if reopen else 'confirm'),
1346
('period_id', '=', period_id),
1351
self.register_reopen(db, id)
1353
self.register_close(db, id)
1357
period_obj.action_reopen_field([period_id])
1359
period_obj.action_close_field([period_id])
1362
period_obj.action_reopen_mission([period_id])
1364
period_obj.action_close_mission([period_id])
1367
period_obj.action_open_period([period_id])
1369
period_obj.action_close_hq([period_id])
1371
def invoice_create_supplier_invoice(self, db, ccy_code=False,
1372
is_refund=False, date=False, partner_id=False,
1373
ad_header_breakdown_data=False,
1374
lines_accounts=[], lines_breakdown_data=False,
1377
create a supplier invoice or
1378
:param ccy_code: ccy code (partner ccy if not set)
1379
:param is_refund: is a refund ? False for a regular invoice
1380
:param date: today if False
1381
:param partner_id: Local Market if False
1382
:param ad_header_breakdown_data: see analytic_distribution_create
1383
:param lines_accounts: list of account codes for each line to generate
1384
:param lines_breakdown_data: {index: [(percent, dest, cc, fp), ]}
1385
index (start by 1) refer to lines_accounts order
1386
the index key allow to pass some lines with AD at line level
1387
keeping using AD at header level for others lines
1388
:return : id of invoice
1392
ai_obj = db.get('account.invoice')
1394
# simulate menu context
1395
context = { 'type': 'in_invoice', 'journal_type': 'purchase', }
1398
itype = 'in_refund' if is_refund else 'in_invoice'
1399
date = date or self.get_orm_date_now()
1403
'is_direct_invoice': False,
1404
'is_inkind_donation': False,
1405
'is_debit_note': False,
1406
'is_intermission': False,
1408
'date_invoice': date, # posting date
1409
'document_date': date,
1413
# via on_change: will set journal
1414
# company_id = db.get('res.users').browse(cr, uid, [uid]).company_id.id
1415
company_id = db.user.company_id.id
1416
vals['company_id'] = company_id
1417
res = ai_obj.onchange_company_id(
1421
itype, # invoice type
1422
False, # invoice line,
1424
if res and res['value']:
1425
vals.update(res['value'])
1428
# via on_change: will set account_id, currency_id
1431
('supplier', '=', True),
1432
('name', '=', 'Local Market'),
1434
partner_id = db.get('res.partner').search(domain)
1436
partner_id != False,
1437
"Partner %s not found" % (str(domain), )
1439
partner_id = partner_id[0]
1440
vals['partner_id'] = partner_id
1442
res = ai_obj.onchange_partner_id(
1444
itype, # invoice type
1446
False, # date_invoice
1447
False, # payment_term
1448
False, # partner_bank_id
1450
False, # is_inkind_donation
1451
False, # is_intermission
1452
False, # is_debit_note
1453
False) # is_direct_invoice
1454
if res and res['value']:
1455
vals.update(res['value'])
1458
# specific ccy instead of partner one
1459
ccy_ids = db.get('res.currency').search([('name', '=', ccy_code)])
1462
"'%s' currency not found" % (ccy_code, )
1464
vals['currency_id'] = ccy_ids[0]
1467
if ad_header_breakdown_data:
1468
vals['analytic_distribution_id'] = \
1469
self.analytic_distribution_create(db,
1470
breakdown_data=ad_header_breakdown_data)
1473
id = ai_obj.create(vals, context)
1479
'account_id': self.get_account_from_code(db, a),
1480
'name': FINANCE_TEST_MASK['invoice_line'] % (tag, id, i + 1,
1482
'price_unit': float(randrange(1, 10)),
1483
'quantity': float(randrange(10, 100)),
1484
}) for i, a in list(enumerate(lines_accounts))
1487
if lines_breakdown_data:
1488
for i in lines_breakdown_data:
1489
line_vals[i-1][2]['analytic_distribution_id'] = \
1490
self.analytic_distribution_create(db,
1491
breakdown_data=lines_breakdown_data[i])
1493
ai_obj.write([id], {'invoice_line': line_vals}, context)
1497
def invoice_validate(self, db, id, out=None):
1499
validate the invoice and return its expense JIs ids list
1500
the invoice must be DRAT
1501
:param out: optional result, if provided filled with:
1502
{ 'ji_counterpart_id': id, 'ji_counterpart_sdref': 'sdref', }
1503
:type out: dict/none
1504
:return : [ji_id, ...]
1506
if not isinstance(id, (int, long, )):
1507
raise FinanceTestException("id parameter expected to be unique id")
1511
ai_model_name = 'account.invoice'
1512
ai_obj = db.get(ai_model_name)
1513
aml_model = 'account.move.line'
1514
aml_obj = db.get(aml_model)
1516
ai = ai_obj.browse(id)
1517
if ai.state != 'draft':
1518
raise FinanceTestException(
1519
"You can not validate non draft invoice")
1522
# - force doc date to posting date (as by default to cur date)
1524
'document_date': self.date2orm(ai.date_invoice),
1526
if not ai.check_total:
1527
vals['check_total'] = ai.amount_total
1528
ai_obj.write([ai.id], vals)
1529
db.exec_workflow(ai_model_name, 'invoice_open', ai.id)
1531
# rebrowse it to refresh after validate to get accounting entries
1532
ai = ai_obj.browse(id)
1534
# get invoice EXPENSE JIs
1535
ji_ids = [ ji.id for ji in ai.move_id.line_id \
1536
if ji.account_id.is_analytic_addicted
1543
# get counterpart JI
1544
ji_ids = [ ji.id for ji in ai.move_id.line_id \
1545
if not ji.account_id.is_analytic_addicted
1548
out['ji_counterpart_id'] = ji_ids[0]
1549
out['ji_counterpart_sdref'] = self.get_record_sdref_from_id(
1550
aml_model, db, ji_ids[0])
1554
def get_jis_by_account(self, db, ji_ids):
1556
get JIs id/sdref breakdown by account code
1557
:param ji_ids: JI ids
1558
:return { 'account_code': [(id, sdref), ], }
1562
if isinstance(ji_ids, (int, long, )):
1566
for ji_br in db.get('account.move.line').browse(ji_ids):
1567
if not ji_br.account_id.code in res:
1568
res[ji_br.account_id.code] = []
1569
res[ji_br.account_id.code].append((ji_br.id,
1570
self.get_record_sdref_from_id(
1571
'account.move.line', db, ji_br.id)))
1575
def get_ji_ajis_by_account(self, db, ji_ids, account_code_filter=False,
1576
cc_code_filter=False):
1578
get base JIs'AJIs id/sdref breakdown by account code
1579
(use for sync cases, REV/COR AJIs are not get, only base AJIs)
1580
:param ji_ids: JI ids
1581
:param account_code_filter: filter results by account code
1582
:param cc_code_filter: filter results by CC code
1583
:return { 'account_code': [(id, sdref), ], } or [(id, sdref), ] if
1584
cc_code_filter is used
1588
if isinstance(ji_ids, (int, long, )):
1590
res = [] if account_code_filter else {}
1593
for ji_br in db.get('account.move.line').browse(ji_ids):
1594
if ji_br.analytic_lines:
1595
for aji in ji_br.analytic_lines:
1596
if account_code_filter and \
1597
aji.general_account_id.code != account_code_filter:
1599
if cc_code_filter and \
1600
aji.cost_center_id.code != cc_code_filter:
1602
if 'COR' in aji.name or 'REV' in aji.name:
1603
# DO NOT GET REV/COR AJIs
1606
if aji.id in get_ajis:
1608
get_ajis.append(aji.id)
1612
self.get_record_sdref_from_id(
1613
'account.analytic.line',
1616
if account_code_filter:
1617
res.append(res_item)
1619
if not ji_br.account_id.code in res:
1620
res[ji_br.account_id.code] = []
1621
res[ji_br.account_id.code].append(res_item)
1625
def get_aji_revs(self, db, aji, cor_level=1):
1627
get aji REVs entries
1628
:param aji: aji id or sdref
1630
:param cor_level: cor level
1631
:type cor_level: int
1632
:return [(id, sdref), ]
1634
if isinstance(aji, str):
1635
aji = self.get_record_id_from_sdref(db, aji)
1637
raise FinanceTestException("aji not found from sdref '%s'" % (
1639
elif not isinstance(aji, (int, long, )):
1640
raise FinanceTestException('invalid arg aji: int or str expected')
1643
od_journal_ids = self.get_journal_ids(db, 'correction',
1644
is_of_instance=False, is_analytic=True)
1645
aal_model = 'account.analytic.line'
1646
aal_obj = db.get(aal_model)
1647
aal_br = aal_obj.browse(aji)
1650
('journal_id', 'in', od_journal_ids),
1651
('general_account_id', '=', aal_br.general_account_id.id),
1654
domain.append(('reversal_origin', '=', aji))
1656
domain.append(('name', '=', "REV - %s" % (aal_br.name, )))
1657
ids = aal_obj.search(domain)
1661
(id, self.get_record_sdref_from_id(aal_model, db, id)) \
1666
def get_aji_cors(self, db, aji, new_account=False, cor_level=1):
1668
get aji CORs entries
1669
:param aji: aji id or sdref
1671
:param new_account: new GL account code expected if was corrected
1672
:type new_account: str/False
1673
:param cor_level: cor level
1674
:type cor_level: int
1675
:return [(id, sdref), ]
1677
if isinstance(aji, str):
1678
aji = self.get_record_id_from_sdref(db, aji)
1680
raise FinanceTestException("aji not found from sdref '%s'" % (
1682
elif not isinstance(aji, (int, long, )):
1683
raise FinanceTestException('invalid arg aji: int or str expected')
1686
od_journal_ids = self.get_journal_ids(db, 'correction',
1687
is_of_instance=False, is_analytic=True)
1688
aal_model = 'account.analytic.line'
1689
aal_obj = db.get(aal_model)
1690
aal_br = aal_obj.browse(aji)
1693
account_id = self.get_account_from_code(db, new_account,
1696
account_id = aal_br.general_account_id.id
1698
ids = aal_obj.search([
1699
#('last_corrected_id', '=', aji),
1700
('journal_id', 'in', od_journal_ids),
1701
('general_account_id', '=', account_id),
1702
('name', '=', "COR%d - %s" % (cor_level, aal_br.name, )),
1706
(id, self.get_record_sdref_from_id(aal_model, db, id)) \
1711
def analytic_account_activate_since(self, db, date):
1712
aaa_obj = db.get('account.analytic.account')
1713
aaa_ids = aaa_obj.search([('parent_id', '!=', False)])
1714
for aaa_br in aaa_obj.browse(aaa_ids):
1715
aaa_obj.write(aaa_br.id, {
1716
'parent_id': aaa_br.parent_id.id,
1720
def check_ji_record_sync_push_pulled(self,
1723
push_not_expected=[],
1724
push_should_deleted=[],
1726
assert_report=True):
1728
JI wrapper for check_records_sync_push_pulled
1729
see unifield_test.py check_records_sync_push_pulled for parameters help
1734
# check fields of expected pulled records
1748
model_ccy = 'res.currency'
1749
model_account = 'account.account'
1751
('account.move', 'move_id'),
1753
(model_ccy, 'currency_id'),
1754
(model_ccy, 'functional_currency_id'),
1756
(model_account, 'account_id'),
1759
return self.check_records_sync_push_pulled(
1760
model='account.move.line',
1762
push_expected=push_expected,
1763
push_not_expected=push_not_expected,
1764
push_should_deleted=push_should_deleted,
1766
fields=fields, fields_m2o=fields_m2o,
1767
assert_report=assert_report,
1768
report_fields=[ 'account_id', 'date', 'document_date', ]
1771
def check_aji_record_sync_push_pulled(self,
1774
push_not_expected=[],
1775
push_should_deleted=[],
1777
assert_report=True):
1779
AJI wrapper for check_records_sync_push_pulled
1780
see unifield_test.py check_records_sync_push_pulled for parameters help
1785
# check fields of expected pulled records
1798
model_ccy = 'res.currency'
1799
model_account = 'account.account'
1800
model_analytic_account = 'account.analytic.account'
1802
(model_ccy, 'currency_id'),
1803
(model_ccy, 'functional_currency_id'),
1805
(model_account, 'general_account_id'),
1807
(model_analytic_account, 'destination_id'),
1808
(model_analytic_account, 'cost_center_id'),
1809
(model_analytic_account, 'account_id'), # funding pool
1812
return self.check_records_sync_push_pulled(
1813
model='account.analytic.line',
1815
push_expected=push_expected,
1816
push_not_expected=push_not_expected,
1817
push_should_deleted=push_should_deleted,
1819
fields=fields, fields_m2o=fields_m2o,
1820
assert_report=assert_report,
1822
'general_account_id', 'cost_center_id', 'account_id',
1823
'date', 'document_date', ]
1826
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: