Merge remote-tracking branch 'odoo/master' into master-wmsstagingmig-jco

This commit is contained in:
Josse Colpaert 2014-06-23 10:06:42 +02:00
commit cf48492570
191 changed files with 1757 additions and 1382 deletions

View File

@ -1,2 +0,0 @@
.*
**/node_modules

View File

@ -26,6 +26,9 @@ from openerp.report import report_sxw
class account_bank_statement(osv.osv):
def create(self, cr, uid, vals, context=None):
if vals.get('name', '/') == '/':
journal_id = vals.get('journal_id', self._default_journal_id(cr, uid, context=context))
vals['name'] = self._compute_default_statement_name(cr, uid, journal_id, context=context)
if 'line_ids' in vals:
for idx, line in enumerate(vals['line_ids']):
line[2]['sequence'] = idx + 1
@ -65,17 +68,14 @@ class account_bank_statement(osv.osv):
return periods[0]
return False
def _compute_default_statement_name(self, cr, uid, context=None):
def _compute_default_statement_name(self, cr, uid, journal_id, context=None):
if context is None:
context = {}
obj_seq = self.pool.get('ir.sequence')
default_journal_id = self._default_journal_id(cr, uid, context=context)
if default_journal_id != False:
period = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, context=context), context=context)
context['fiscalyear_id'] = period.fiscalyear_id.id
journal = self.pool.get('account.journal').browse(cr, uid, default_journal_id, None)
return obj_seq.next_by_id(cr, uid, journal.sequence_id.id, context=context)
return obj_seq.next_by_code(cr, uid, 'account.bank.statement', context=context)
period = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, context=context), context=context)
context['fiscalyear_id'] = period.fiscalyear_id.id
journal = self.pool.get('account.journal').browse(cr, uid, journal_id, None)
return obj_seq.next_by_id(cr, uid, journal.sequence_id.id, context=context)
def _currency(self, cursor, user, ids, name, args, context=None):
res = {}
@ -150,7 +150,7 @@ class account_bank_statement(osv.osv):
}
_defaults = {
'name': _compute_default_statement_name,
'name': '/',
'date': fields.date.context_today,
'state': 'draft',
'journal_id': _default_journal_id,
@ -213,11 +213,8 @@ class account_bank_statement(osv.osv):
def _get_counter_part_account(sefl, cr, uid, st_line, context=None):
"""Retrieve the account to use in the counterpart move.
This method may be overridden to implement custom move generation (making sure to
call super() to establish a clean extension chain).
:param browse_record st_line: account.bank.statement.line record to
create the move from.
:param browse_record st_line: account.bank.statement.line record to create the move from.
:return: int/long of the account.account to use as counterpart
"""
if st_line.amount >= 0:
@ -226,26 +223,19 @@ class account_bank_statement(osv.osv):
def _get_counter_part_partner(sefl, cr, uid, st_line, context=None):
"""Retrieve the partner to use in the counterpart move.
This method may be overridden to implement custom move generation (making sure to
call super() to establish a clean extension chain).
:param browse_record st_line: account.bank.statement.line record to
create the move from.
:param browse_record st_line: account.bank.statement.line record to create the move from.
:return: int/long of the res.partner to use as counterpart
"""
return st_line.partner_id and st_line.partner_id.id or False
def _prepare_bank_move_line(self, cr, uid, st_line, move_id, amount, company_currency_id, context=None):
"""Compute the args to build the dict of values to create the counter part move line from a
statement line by calling the _prepare_move_line_vals. This method may be
overridden to implement custom move generation (making sure to call super() to
establish a clean extension chain).
statement line by calling the _prepare_move_line_vals.
:param browse_record st_line: account.bank.statement.line record to
create the move from.
:param browse_record st_line: account.bank.statement.line record to create the move from.
:param int/long move_id: ID of the account.move to link the move line
:param float amount: amount of the move line
:param int/long account_id: ID of account to use as counter part
:param int/long company_currency_id: ID of currency of the concerned company
:return: dict of value to create() the bank account.move.line
"""
@ -258,7 +248,6 @@ class account_bank_statement(osv.osv):
if st_line.statement_id.currency.id != company_currency_id:
amt_cur = st_line.amount
cur_id = st_line.currency_id or st_line.statement_id.currency.id
# TODO : FIXME the amount should be in the journal currency
if st_line.currency_id and st_line.amount_currency:
amt_cur = st_line.amount_currency
cur_id = st_line.currency_id.id
@ -269,9 +258,7 @@ class account_bank_statement(osv.osv):
def _prepare_move_line_vals(self, cr, uid, st_line, move_id, debit, credit, currency_id=False,
amount_currency=False, account_id=False, partner_id=False, context=None):
"""Prepare the dict of values to create the move line from a
statement line. All non-mandatory args will replace the default computed one.
This method may be overridden to implement custom move generation (making sure to
call super() to establish a clean extension chain).
statement line.
:param browse_record st_line: account.bank.statement.line record to
create the move from.
@ -342,21 +329,29 @@ class account_bank_statement(osv.osv):
move_ids.append(st_line.journal_entry_id.id)
self.pool.get('account.move').post(cr, uid, move_ids, context=context)
self.message_post(cr, uid, [st.id], body=_('Statement %s confirmed, journal items were created.') % (st.name,), context=context)
self.link_bank_to_partner(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state':'confirm'}, context=context)
def button_cancel(self, cr, uid, ids, context=None):
done = []
account_move_obj = self.pool.get('account.move')
reconcile_pool = self.pool.get('account.move.reconcile')
move_line_pool = self.pool.get('account.move.line')
move_ids = []
for st in self.browse(cr, uid, ids, context=context):
if st.state=='draft':
continue
move_ids = []
for line in st.line_ids:
move_ids += [x.id for x in line.move_ids]
if line.journal_entry_id:
move_ids.append(line.journal_entry_id.id)
for aml in line.journal_entry_id.line_id:
if aml.reconcile_id:
move_lines = [l.id for l in aml.reconcile_id.line_id]
move_lines.remove(aml.id)
reconcile_pool.unlink(cr, uid, [aml.reconcile_id.id], context=context)
if len(move_lines) >= 2:
move_line_pool.reconcile_partial(cr, uid, move_lines, 'auto', context=context)
if move_ids:
account_move_obj.button_cancel(cr, uid, move_ids, context=context)
account_move_obj.unlink(cr, uid, move_ids, context)
done.append(st.id)
return self.write(cr, uid, done, {'state':'draft'}, context=context)
return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
def _compute_balance_end_real(self, cr, uid, journal_id, context=None):
res = False
@ -417,18 +412,35 @@ class account_bank_statement(osv.osv):
def number_of_lines_reconciled(self, cr, uid, id, context=None):
bsl_obj = self.pool.get('account.bank.statement.line')
return bsl_obj.search_count(cr, uid, [('statement_id','=',id), ('journal_entry_id','!=',False)], context=context)
return bsl_obj.search_count(cr, uid, [('statement_id', '=', id), ('journal_entry_id', '!=', False)], context=context)
def get_format_currency_js_function(self, cr, uid, id, context=None):
""" Returns a string that can be used to instanciate a javascript function.
That function formats a number according to the statement's journal currency """
That function formats a number according to the statement line's currency or the statement currency"""
company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id
currency_obj = id and self.browse(cr, uid, id, context=context).journal_id.currency or company_currency
st = id and self.browse(cr, uid, id, context=context)
if not st:
return
statement_currency = st.journal_id.currency or company_currency
digits = 2 # TODO : from currency_obj
if currency_obj.position == 'after':
return "return amount.toFixed("+str(digits)+") + ' "+currency_obj.symbol+"';"
elif currency_obj.position == 'before':
return "return '"+currency_obj.symbol+" ' + amount.toFixed("+str(digits)+");"
function = ""
done_currencies = []
for st_line in st.line_ids:
st_line_currency = st_line.currency_id or statement_currency
if st_line_currency.id not in done_currencies:
if st_line_currency.position == 'after':
return_str = "return amount.toFixed(" + str(digits) + ") + ' " + st_line_currency.symbol + "';"
else:
return_str = "return '" + st_line_currency.symbol + " ' + amount.toFixed(" + str(digits) + ");"
function += "if (currency_id === " + str(st_line_currency.id) + "){ " + return_str + " }"
done_currencies.append(st_line_currency.id)
return function
def link_bank_to_partner(self, cr, uid, ids, context=None):
for statement in self.browse(cr, uid, ids, context=context):
for st_line in statement.line_ids:
if st_line.bank_account_id and st_line.partner_id and st_line.bank_account_id.partner_id.id != st_line.partner_id.id:
self.pool.get('res.partner.bank').write(cr, uid, [st_line.bank_account_id.id], {'partner_id': st_line.partner_id.id}, context=context)
class account_bank_statement_line(osv.osv):
@ -444,35 +456,40 @@ class account_bank_statement_line(osv.osv):
}
for mv_line in reconciliation_data['reconciliation_proposition']:
mv_line_ids_selected.append(mv_line['id'])
ret.append(reconciliation_data);
ret.append(reconciliation_data)
# Check if, now that 'candidate' move lines were selected, there are moves left for statement lines
for reconciliation_data in ret:
if not reconciliation_data['st_line']['has_no_partner']:
if self.get_move_lines_counterparts(cr, uid, reconciliation_data['st_line']['id'], excluded_ids=mv_line_ids_selected, count=True, context=context) == 0:
reconciliation_data['st_line']['no_match'] = True
#for reconciliation_data in ret:
# if not reconciliation_data['st_line']['has_no_partner']:
# st_line = self.browse(cr, uid, reconciliation_data['st_line']['id'], context=context)
# if not self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=mv_line_ids_selected, count=True, context=context):
# reconciliation_data['st_line']['no_match'] = True
return ret
def get_statement_line_for_reconciliation(self, cr, uid, id, context=None):
""" Returns the data required by the bank statement reconciliation use case """
line = self.browse(cr, uid, id, context=context)
statement_currency = line.journal_id.currency or line.journal_id.company_id.currency_id
amount = line.amount
rml_parser = report_sxw.rml_parse(cr, uid, 'statement_line_widget', context=context)
amount_str = line.amount > 0 and line.amount or -line.amount
amount_str = rml_parser.formatLang(amount_str, currency_obj=statement_currency)
amount_currency_str = ""
if line.amount_currency and line.currency_id:
amount_currency_str = rml_parser.formatLang(line.amount_currency, currency_obj=line.currency_id)
amount_currency_str = amount_str
amount_str = rml_parser.formatLang(line.amount_currency, currency_obj=line.currency_id)
amount = line.amount_currency
dict = {
data = {
'id': line.id,
'ref': line.ref,
'note': line.note or "",
'name': line.name,
'date': line.date,
'amount': line.amount,
'amount': amount,
'amount_str': amount_str,
'no_match': self.get_move_lines_counterparts(cr, uid, id, count=True, context=context) == 0 and line.partner_id.id,
'currency_id': line.currency_id.id or statement_currency.id,
'no_match': self.get_move_lines_counterparts(cr, uid, line, count=True, context=context) == 0,
'partner_id': line.partner_id.id,
'statement_id': line.statement_id.id,
'account_code': line.journal_id.default_debit_account_id.code,
@ -482,93 +499,116 @@ class account_bank_statement_line(osv.osv):
'has_no_partner': not line.partner_id.id,
}
if line.partner_id.id:
if line.amount > 0:
dict['open_balance_account_id'] = line.partner_id.property_account_receivable.id
else:
dict['open_balance_account_id'] = line.partner_id.property_account_payable.id
return dict
data['open_balance_account_id'] = line.partner_id.property_account_payable.id
if amount > 0:
data['open_balance_account_id'] = line.partner_id.property_account_receivable.id
return data
def search_structured_com(self, cr, uid, st_line, context=None):
if not st_line.ref:
return
domain = [('ref', '=', st_line.ref)]
if st_line.partner_id:
domain += [('partner_id', '=', st_line.partner_id.id)]
ids = self.pool.get('account.move.line').search(cr, uid, domain, limit=1, context=context)
return ids and ids[0] or False
def get_reconciliation_proposition(self, cr, uid, id, excluded_ids=[], context=None):
""" Returns move lines that constitute the best guess to reconcile a statement line. """
st_line = self.browse(cr, uid, id, context=context)
company_currency = st_line.journal_id.company_id.currency_id.id
statement_currency = st_line.journal_id.currency.id or company_currency
# either use the unsigned debit/credit fields or the signed amount_currency field
sign = 1
if statement_currency == company_currency:
amount_field = 'credit'
if st_line.amount > 0:
amount_field = 'debit'
else:
amount_field = 'credit'
else:
amount_field = 'amount_currency'
if st_line.amount < 0:
sign = -1
# look for structured communication
exact_match_id = self.search_structured_com(cr, uid, st_line, context=context)
if exact_match_id:
return self.make_counter_part_lines(cr, uid, st_line, [exact_match_id], count=False, context=context)
#we don't propose anything if there is no partner detected
if not st_line.partner_id.id:
return []
# look for exact match
exact_match_id = self.get_move_lines_counterparts(cr, uid, id, excluded_ids=excluded_ids, limit=1, additional_domain=[(amount_field,'=',(sign*st_line.amount))])
exact_match_id = self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=excluded_ids, limit=1, additional_domain=[(amount_field, '=', (sign * st_line.amount))])
if exact_match_id:
return exact_match_id
# select oldest move lines
if sign == -1:
mv_lines = self.get_move_lines_counterparts(cr, uid, id, excluded_ids=excluded_ids, limit=50, additional_domain=[(amount_field,'<',0)])
mv_lines = self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=excluded_ids, limit=50, additional_domain=[(amount_field, '<', 0)])
else:
mv_lines = self.get_move_lines_counterparts(cr, uid, id, excluded_ids=excluded_ids, limit=50, additional_domain=[(amount_field,'>',0)])
mv_lines = self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids=excluded_ids, limit=50, additional_domain=[(amount_field, '>', 0)])
ret = []
total = 0
# get_move_lines_counterparts inverts debit and credit
amount_field = 'debit' if amount_field == 'credit' else 'credit'
for line in mv_lines:
if total + line[amount_field] <= st_line.amount:
if total + line[amount_field] <= abs(st_line.amount):
ret.append(line)
total += line[amount_field]
else:
if total >= abs(st_line.amount):
break
return ret
def get_move_lines_counterparts(self, cr, uid, id, excluded_ids=[], str="", offset=0, limit=None, count=False, additional_domain=[], context=None):
def get_move_lines_counterparts_id(self, cr, uid, st_line_id, excluded_ids=[], filter_str="", offset=0, limit=None, count=False, additional_domain=[], context=None):
st_line = self.browse(cr, uid, st_line_id, context=context)
return self.get_move_lines_counterparts(cr, uid, st_line, excluded_ids, filter_str, offset, limit, count, additional_domain, context=context)
def get_move_lines_counterparts(self, cr, uid, st_line, excluded_ids=[], filter_str="", offset=0, limit=None, count=False, additional_domain=[], context=None):
""" Find the move lines that could be used to reconcile a statement line and returns the counterpart that could be created to reconcile them
If count is true, only returns the count.
:param integer id: the id of the statement line
:param st_line: the browse record of the statement line
:param integers list excluded_ids: ids of move lines that should not be fetched
:param string str: string to filter lines
:param string filter_str: string to filter lines
:param integer offset: offset of the request
:param integer limit: number of lines to fetch
:param boolean count: just return the number of records
:param tuples list domain: additional domain restrictions
"""
if context is None:
context = {}
rml_parser = report_sxw.rml_parse(cr, uid, 'statement_line_counterpart_widget', context=context)
st_line = self.browse(cr, uid, id, context=context)
company_currency = st_line.journal_id.company_id.currency_id
statement_currency = st_line.journal_id.currency or company_currency
mv_line_pool = self.pool.get('account.move.line')
currency_obj = self.pool.get('res.currency')
domain = additional_domain + [
('partner_id', '=', st_line.partner_id.id),
('reconcile_id', '=', False),
('state','=','valid'),
'|',('account_id.type', '=', 'receivable'),
('account_id.type', '=', 'payable'), #Let the front-end warn the user if he tries to mix payable and receivable in the same reconciliation
('state', '=', 'valid'),
]
if st_line.partner_id.id:
domain += [('partner_id', '=', st_line.partner_id.id),
'|', ('account_id.type', '=', 'receivable'),
('account_id.type', '=', 'payable')]
else:
domain += [('account_id.reconcile', '=', True)]
#domain += [('account_id.reconcile', '=', True), ('account_id.type', '=', 'other')]
if excluded_ids:
domain.append(('id', 'not in', excluded_ids))
if str:
domain += ['|', ('move_id.name', 'ilike', str), ('move_id.ref', 'ilike', str)]
if filter_str:
if not st_line.partner_id:
domain += [ '|', ('partner_id.name', 'ilike', filter_str)]
domain += ['|', ('move_id.name', 'ilike', filter_str), ('move_id.ref', 'ilike', filter_str)]
line_ids = mv_line_pool.search(cr, uid, domain, offset=offset, limit=limit, order="date_maturity asc, id asc", context=context)
return self.make_counter_part_lines(cr, uid, st_line, line_ids, count=count, context=context)
def make_counter_part_lines(self, cr, uid, st_line, line_ids, count=False, context=None):
if context is None:
context = {}
mv_line_pool = self.pool.get('account.move.line')
currency_obj = self.pool.get('res.currency')
company_currency = st_line.journal_id.company_id.currency_id
statement_currency = st_line.journal_id.currency or company_currency
rml_parser = report_sxw.rml_parse(cr, uid, 'statement_line_counterpart_widget', context=context)
#partially reconciled lines can be displayed only once
reconcile_partial_ids = []
ids = mv_line_pool.search(cr, uid, domain, offset=offset, limit=limit, order="date_maturity asc, id asc", context=context)
if count:
nb_lines = 0
for line in mv_line_pool.browse(cr, uid, ids, context=context):
for line in mv_line_pool.browse(cr, uid, line_ids, context=context):
if line.reconcile_partial_id and line.reconcile_partial_id.id in reconcile_partial_ids:
continue
nb_lines += 1
@ -577,7 +617,7 @@ class account_bank_statement_line(osv.osv):
return nb_lines
else:
ret = []
for line in mv_line_pool.browse(cr, uid, ids, context=context):
for line in mv_line_pool.browse(cr, uid, line_ids, context=context):
if line.reconcile_partial_id and line.reconcile_partial_id.id in reconcile_partial_ids:
continue
amount_currency_str = ""
@ -595,8 +635,12 @@ class account_bank_statement_line(osv.osv):
'period_name': line.period_id.name,
'journal_name': line.journal_id.name,
'amount_currency_str': amount_currency_str,
'partner_id': line.partner_id.id,
'partner_name': line.partner_id.name,
'has_no_partner': not bool(st_line.partner_id.id),
}
if statement_currency.id != company_currency.id and line.currency_id and line.currency_id.id == statement_currency.id:
st_line_currency = st_line.currency_id or statement_currency
if st_line.currency_id and line.currency_id and line.currency_id.id == st_line.currency_id.id:
if line.amount_residual_currency < 0:
ret_line['debit'] = 0
ret_line['credit'] = -line.amount_residual_currency
@ -613,21 +657,46 @@ class account_bank_statement_line(osv.osv):
ret_line['credit'] = line.amount_residual if line.debit != 0 else 0
ctx = context.copy()
ctx.update({'date': st_line.date})
ret_line['debit'] = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, ret_line['debit'], context=ctx)
ret_line['credit'] = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, ret_line['credit'], context=ctx)
ret_line['debit_str'] = rml_parser.formatLang(ret_line['debit'], currency_obj=statement_currency)
ret_line['credit_str'] = rml_parser.formatLang(ret_line['credit'], currency_obj=statement_currency)
ret_line['debit'] = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, ret_line['debit'], context=ctx)
ret_line['credit'] = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, ret_line['credit'], context=ctx)
ret_line['debit_str'] = rml_parser.formatLang(ret_line['debit'], currency_obj=st_line_currency)
ret_line['credit_str'] = rml_parser.formatLang(ret_line['credit'], currency_obj=st_line_currency)
ret.append(ret_line)
if line.reconcile_partial_id:
reconcile_partial_ids.append(line.reconcile_partial_id.id)
return ret
def get_currency_rate_line(self, cr, uid, st_line, currency_diff, move_id, context=None):
if currency_diff < 0:
account_id = st_line.company_id.expense_currency_exchange_account_id.id
if not account_id:
raise osv.except_osv(_('Insufficient Configuration!'), _("You should configure the 'Loss Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
else:
account_id = st_line.company_id.income_currency_exchange_account_id.id
if not account_id:
raise osv.except_osv(_('Insufficient Configuration!'), _("You should configure the 'Gain Exchange Rate Account' in the accounting settings, to manage automatically the booking of accounting entries related to differences between exchange rates."))
return {
'move_id': move_id,
'name': _('change') + ': ' + (st_line.name or '/'),
'period_id': st_line.statement_id.period_id.id,
'journal_id': st_line.journal_id.id,
'partner_id': st_line.partner_id.id,
'company_id': st_line.company_id.id,
'statement_id': st_line.statement_id.id,
'debit': currency_diff < 0 and -currency_diff or 0,
'credit': currency_diff > 0 and currency_diff or 0,
'date': st_line.date,
'account_id': account_id
}
def process_reconciliation(self, cr, uid, id, mv_line_dicts, context=None):
""" Creates a move line for each item of mv_line_dicts and for the statement line. Reconcile a new move line with its counterpart_move_line_id if specified. Finally, mark the statement line as reconciled by putting the newly created move id in the column journal_entry_id.
:param int id: id of the bank statement line
:param list of dicts mv_line_dicts: move lines to create. If counterpart_move_line_id is specified, reconcile with it
"""
if context is None:
context = {}
st_line = self.browse(cr, uid, id, context=context)
company_currency = st_line.journal_id.company_id.currency_id
statement_currency = st_line.journal_id.currency or company_currency
@ -637,7 +706,7 @@ class account_bank_statement_line(osv.osv):
currency_obj = self.pool.get('res.currency')
# Checks
if st_line.journal_entry_id.id != False:
if st_line.journal_entry_id.id:
raise osv.except_osv(_('Error!'), _('The bank statement line was already reconciled.'))
for mv_line_dict in mv_line_dicts:
for field in ['debit', 'credit', 'amount_currency']:
@ -657,37 +726,50 @@ class account_bank_statement_line(osv.osv):
amount = currency_obj.compute(cr, uid, st_line.statement_id.currency.id, company_currency.id, st_line.amount, context=context)
bank_st_move_vals = bs_obj._prepare_bank_move_line(cr, uid, st_line, move_id, amount, company_currency.id, context=context)
aml_obj.create(cr, uid, bank_st_move_vals, context=context)
st_line_currency_rate = bank_st_move_vals['amount_currency'] and statement_currency.id == company_currency.id and (bank_st_move_vals['amount_currency'] / st_line.amount) or False
st_line_currency = bank_st_move_vals['currency_id']
# Complete the dicts
st_line_statement_id = st_line.statement_id.id
st_line_journal_id = st_line.journal_id.id
st_line_partner_id = st_line.partner_id.id
st_line_company_id = st_line.company_id.id
st_line_period_id = st_line.statement_id.period_id.id
st_line_currency = st_line.currency_id or statement_currency
st_line_currency_rate = st_line.currency_id and statement_currency.id == company_currency.id and (st_line.amount_currency / st_line.amount) or False
to_create = []
for mv_line_dict in mv_line_dicts:
mv_line_dict['ref'] = move_name
mv_line_dict['move_id'] = move_id
mv_line_dict['period_id'] = st_line_period_id
mv_line_dict['journal_id'] = st_line_journal_id
mv_line_dict['partner_id'] = st_line_partner_id
mv_line_dict['company_id'] = st_line_company_id
mv_line_dict['statement_id'] = st_line_statement_id
mv_line_dict['period_id'] = st_line.statement_id.period_id.id
mv_line_dict['journal_id'] = st_line.journal_id.id
mv_line_dict['company_id'] = st_line.company_id.id
mv_line_dict['statement_id'] = st_line.statement_id.id
if mv_line_dict.get('counterpart_move_line_id'):
mv_line = aml_obj.browse(cr, uid, mv_line_dict['counterpart_move_line_id'], context=context)
mv_line_dict['account_id'] = mv_line.account_id.id
if statement_currency.id != company_currency.id:
if st_line_currency.id != company_currency.id:
mv_line_dict['amount_currency'] = mv_line_dict['debit'] - mv_line_dict['credit']
mv_line_dict['currency_id'] = statement_currency.id
mv_line_dict['debit'] = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['debit'])
mv_line_dict['credit'] = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['credit'])
elif st_line_currency and st_line_currency_rate:
mv_line_dict['amount_currency'] = self.pool.get('res.currency').round(cr, uid, st_line.currency_id, (mv_line_dict['debit'] - mv_line_dict['credit']) * st_line_currency_rate)
mv_line_dict['currency_id'] = st_line_currency
mv_line_dict['currency_id'] = st_line_currency.id
if st_line.currency_id and statement_currency.id == company_currency.id and st_line_currency_rate:
debit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['debit'] / st_line_currency_rate)
credit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['credit'] / st_line_currency_rate)
else:
debit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=context)
credit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=context)
if mv_line_dict.get('counterpart_move_line_id'):
#post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line
ctx = context.copy()
ctx['date'] = mv_line.date
debit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx)
credit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx)
mv_line_dict['credit'] = credit_at_old_rate
mv_line_dict['debit'] = debit_at_old_rate
if debit_at_old_rate - debit_at_current_rate:
currency_diff = debit_at_current_rate - debit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context))
if credit_at_old_rate - credit_at_current_rate:
currency_diff = credit_at_current_rate - credit_at_old_rate
to_create.append(self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context))
else:
mv_line_dict['debit'] = debit_at_current_rate
mv_line_dict['credit'] = credit_at_current_rate
to_create.append(mv_line_dict)
# Create move lines
move_line_pairs_to_reconcile = []
for mv_line_dict in mv_line_dicts:
for mv_line_dict in to_create:
counterpart_move_line_id = None # NB : this attribute is irrelevant for aml_obj.create() and needs to be removed from the dict
if mv_line_dict.get('counterpart_move_line_id'):
counterpart_move_line_id = mv_line_dict['counterpart_move_line_id']
@ -723,7 +805,7 @@ class account_bank_statement_line(osv.osv):
'bank_account_id': fields.many2one('res.partner.bank','Bank Account'),
'statement_id': fields.many2one('account.bank.statement', 'Statement', select=True, required=True, ondelete='cascade'),
'journal_id': fields.related('statement_id', 'journal_id', type='many2one', relation='account.journal', string='Journal', store=True, readonly=True),
'ref': fields.char('Reference', size=32),
'ref': fields.char('Structured Communication'),
'note': fields.text('Notes'),
'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of bank statement lines."),
'company_id': fields.related('statement_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),

View File

@ -295,7 +295,8 @@ class account_invoice(osv.osv):
},
multi='all'),
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}, track_visibility='always'),
'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]},
domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]"),
'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean',

View File

@ -127,8 +127,8 @@ class account_move_line(osv.osv):
if move_line.reconcile_id:
continue
if not move_line.account_id.type in ('payable', 'receivable'):
#this function does not suport to be used on move lines not related to payable or receivable accounts
if not move_line.account_id.reconcile:
#this function does not suport to be used on move lines not related to a reconcilable account
continue
if move_line.currency_id:
@ -741,6 +741,8 @@ class account_move_line(osv.osv):
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
if context is None:
context = {}
if context.get('fiscalyear'):
args.append(('period_id.fiscalyear_id', '=', context.get('fiscalyear', False)))
if context and context.get('next_partner_only', False):
if not context.get('partner_id', False):
partner = self.list_partners_to_reconcile(cr, uid, context=context)
@ -823,7 +825,7 @@ class account_move_line(osv.osv):
'line_partial_ids': map(lambda x: (4,x,False), merges+unmerge)
}, context=context)
move_rec_obj.reconcile_partial_check(cr, uid, [r_id] + merges_rec, context=context)
return True
return r_id
def reconcile(self, cr, uid, ids, type='auto', writeoff_acc_id=False, writeoff_period_id=False, writeoff_journal_id=False, context=None):
account_obj = self.pool.get('account.account')

View File

@ -351,7 +351,7 @@
<act_window
id="action_account_items"
name="Journal Items"
context="{'search_default_account_id': [active_id]}"
context="{'search_default_account_id': [active_id], 'fiscalyear': context.get('fiscalyear')}"
res_model="account.move.line"
src_model="account.account"
key2="tree_but_open"/>

View File

@ -15,7 +15,7 @@
<field name="balance_end_real" eval="3707.58"/>
</record>
<record id="demo_bank_statement_line_1" model="account.bank.statement.line">
<field name="ref">001</field>
<field name="ref"></field>
<field name="statement_id" ref="demo_bank_statement_1"/>
<field name="sequence" eval="1"/>
<field name="company_id" ref="base.main_company"/>
@ -26,7 +26,7 @@
<field name="partner_id" ref="base.res_partner_9"/>
</record>
<record id="demo_bank_statement_line_2" model="account.bank.statement.line">
<field name="ref">002</field>
<field name="ref">SAJ2014002</field>
<field name="statement_id" ref="demo_bank_statement_1"/>
<field name="sequence" eval="2"/>
<field name="company_id" ref="base.main_company"/>
@ -37,7 +37,7 @@
<field name="partner_id" ref="base.res_partner_9"/>
</record>
<record id="demo_bank_statement_line_3" model="account.bank.statement.line">
<field name="ref">003</field>
<field name="ref"></field>
<field name="statement_id" ref="demo_bank_statement_1"/>
<field name="sequence" eval="3"/>
<field name="company_id" ref="base.main_company"/>
@ -47,7 +47,7 @@
<field name="date" eval="time.strftime('%Y')+'-01-01'"/>
</record>
<record id="demo_bank_statement_line_4" model="account.bank.statement.line">
<field name="ref">004</field>
<field name="ref"></field>
<field name="statement_id" ref="demo_bank_statement_1"/>
<field name="sequence" eval="4"/>
<field name="company_id" ref="base.main_company"/>

View File

@ -120,8 +120,6 @@
cursor: pointer; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line.no_match:not(.no_partner) .toggle_match {
visibility: hidden !important; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line.no_partner .partner_name, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line.no_partner .line_open_balance {
display: none !important; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line > table > tbody > tr:nth-child(1) > td table {
margin-bottom: 10px; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line table.details td:first-child {
@ -202,10 +200,6 @@
cursor: pointer; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(6) {
border-left: 1px solid black; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr.initial_line > td:nth-child(5) {
border-top: 1px solid black; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr.initial_line > td:nth-child(6) {
border-top: 1px solid black; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match .match_controls {
padding: 0 0 5px 18px; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match .match_controls .filter {

View File

@ -194,12 +194,6 @@ $initialLineBackground: #f0f0f0;
}
}
&.no_partner {
.partner_name, .line_open_balance {
display: none !important;
}
}
/* gap between accounting_view and action view */
> table > tbody > tr:nth-child(1) > td table {
margin-bottom: 10px;
@ -341,10 +335,6 @@ $initialLineBackground: #f0f0f0;
// accounting "T"
td:nth-child(6) { border-left: $accountingBorder; }
tr.initial_line > td {
&:nth-child(5) { border-top: $accountingBorder; }
&:nth-child(6) { border-top: $accountingBorder; }
}
}

View File

@ -171,7 +171,7 @@ openerp.account = function (instance) {
deferred_promises.push(self.model_bank_statement
.call("get_format_currency_js_function", [self.statement_id])
.then(function(data){
self.formatCurrency = new Function("amount", data);
self.formatCurrency = new Function("amount, currency_id", data);
})
);
@ -245,7 +245,7 @@ openerp.account = function (instance) {
keyboardShortcutsHandler: function(e) {
var self = this;
if (e.which === 13 && (e.ctrlKey || e.metaKey)) {
if ((e.which === 13 || e.which === 10) && (e.ctrlKey || e.metaKey)) {
$.each(self.getChildren(), function(i, o){
if (o.is_valid && o.persistAndDestroy()) {
self.lines_reconciled_with_ctrl_enter++;
@ -789,6 +789,9 @@ openerp.account = function (instance) {
line.q_amount = (line.debit !== 0 ? "- "+line.q_debit : "") + (line.credit !== 0 ? line.q_credit : "");
line.q_popover = QWeb.render("bank_statement_reconciliation_move_line_details", {line: line});
line.q_label = line.name;
if (line.has_no_partner){
line.q_label = line.partner_name + ': ' +line.q_label;
}
// WARNING : pretty much of a ugly hack
// The value of account_move.ref is either the move's communication or it's name without the slashes
@ -981,6 +984,7 @@ openerp.account = function (instance) {
lineOpenBalanceClickHandler: function() {
var self = this;
if (self.get("mode") === "create") {
self.addLineBeingEdited();
self.set("mode", "match");
} else {
self.set("mode", "create");
@ -1038,7 +1042,8 @@ openerp.account = function (instance) {
var slice_start = self.get("pager_index") * self.max_move_lines_displayed;
var slice_end = (self.get("pager_index")+1) * self.max_move_lines_displayed;
_( _.filter(self.mv_lines_deselected, function(o){
return o.name.indexOf(self.filter) !== -1 || o.ref.indexOf(self.filter) !== -1 })
return o.q_label.indexOf(self.filter) !== -1 || (o.ref && o.ref.indexOf(self.filter) !== -1)
})
.slice(slice_start, slice_end)).each(function(line){
var $line = $(QWeb.render("bank_statement_reconciliation_move_line", {line: line, selected: false}));
self.bindPopoverTo($line.find(".line_info_button"));
@ -1057,7 +1062,6 @@ openerp.account = function (instance) {
updatePagerControls: function() {
var self = this;
if (self.get("pager_index") === 0)
self.$(".pager_control_left").addClass("disabled");
else
@ -1075,7 +1079,7 @@ openerp.account = function (instance) {
balanceChanged: function() {
var self = this;
var balance = self.get("balance");
self.$(".tbody_open_balance").empty();
// Special case hack : no identified partner
if (self.st_line.has_no_partner) {
if (Math.abs(balance).toFixed(3) === "0.000") {
@ -1088,19 +1092,23 @@ openerp.account = function (instance) {
self.$(".button_ok").attr("disabled", "disabled");
self.$(".button_ok").text("OK");
self.is_valid = false;
var debit = (balance > 0 ? self.formatCurrency(balance, self.st_line.currency_id) : "");
var credit = (balance < 0 ? self.formatCurrency(-1*balance, self.st_line.currency_id) : "");
var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {debit: debit, credit: credit, account_code: self.map_account_id_code[self.st_line.open_balance_account_id]}));
$line.find('.js_open_balance')[0].innerHTML = "Choose counterpart";
self.$(".tbody_open_balance").append($line);
}
return;
}
self.$(".tbody_open_balance").empty();
if (Math.abs(balance).toFixed(3) === "0.000") {
self.$(".button_ok").addClass("oe_highlight");
self.$(".button_ok").text("OK");
} else {
self.$(".button_ok").removeClass("oe_highlight");
self.$(".button_ok").text("Keep open");
var debit = (balance > 0 ? self.formatCurrency(balance) : "");
var credit = (balance < 0 ? self.formatCurrency(-1*balance) : "");
var debit = (balance > 0 ? self.formatCurrency(balance, self.st_line.currency_id) : "");
var credit = (balance < 0 ? self.formatCurrency(-1*balance, self.st_line.currency_id) : "");
var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {debit: debit, credit: credit, account_code: self.map_account_id_code[self.st_line.open_balance_account_id]}));
self.$(".tbody_open_balance").append($line);
}
@ -1111,21 +1119,15 @@ openerp.account = function (instance) {
self.$(".action_pane.active").removeClass("active");
// Special case hack : if no_partner, either inactive or create
// Special case hack : if no_partner and mode == inactive
if (self.st_line.has_no_partner) {
if (self.get("mode") === "inactive") {
self.$(".match").slideUp(self.animation_speed);
self.$(".create").slideUp(self.animation_speed);
self.$(".toggle_match").removeClass("visible_toggle");
self.el.dataset.mode = "inactive";
} else {
self.initializeCreateForm();
self.$(".match").slideUp(self.animation_speed);
self.$(".create").slideDown(self.animation_speed);
self.$(".toggle_match").addClass("visible_toggle");
self.el.dataset.mode = "create";
}
return;
return;
}
}
if (self.get("mode") === "inactive") {
@ -1198,6 +1200,8 @@ openerp.account = function (instance) {
var self = this;
var line_created_being_edited = self.get("line_created_being_edited");
line_created_being_edited[0][elt.corresponding_property] = val.newValue;
line_created_being_edited[0].currency_id = self.st_line.currency_id;
// Specific cases
if (elt === self.account_id_field)
@ -1215,7 +1219,7 @@ openerp.account = function (instance) {
var tax = data.taxes[0];
var tax_account_id = (amount > 0 ? tax.account_collected_id : tax.account_paid_id)
line_created_being_edited[0].amount = (data.total.toFixed(3) === amount.toFixed(3) ? amount : data.total);
line_created_being_edited[1] = {id: line_created_being_edited[0].id, account_id: tax_account_id, account_num: self.map_account_id_code[tax_account_id], label: tax.name, amount: tax.amount, no_remove_action: true};
line_created_being_edited[1] = {id: line_created_being_edited[0].id, account_id: tax_account_id, account_num: self.map_account_id_code[tax_account_id], label: tax.name, amount: tax.amount, no_remove_action: true, currency_id: self.st_line.currency_id};
}
);
} else {
@ -1228,10 +1232,9 @@ openerp.account = function (instance) {
$.when(deferred_tax).then(function(){
// Format amounts
if (line_created_being_edited[0].amount)
line_created_being_edited[0].amount_str = self.formatCurrency(Math.abs(line_created_being_edited[0].amount));
line_created_being_edited[0].amount_str = self.formatCurrency(Math.abs(line_created_being_edited[0].amount), line_created_being_edited[0].currency_id);
if (line_created_being_edited[1] && line_created_being_edited[1].amount)
line_created_being_edited[1].amount_str = self.formatCurrency(Math.abs(line_created_being_edited[1].amount));
line_created_being_edited[1].amount_str = self.formatCurrency(Math.abs(line_created_being_edited[1].amount), line_created_being_edited[0].currency_id);
self.set("line_created_being_edited", line_created_being_edited);
self.createdLinesChanged(); // TODO For some reason, previous line doesn't trigger change handler
});
@ -1268,10 +1271,10 @@ openerp.account = function (instance) {
line.initial_amount = line.debit !== 0 ? line.debit : -1 * line.credit;
if (balance < 0) {
line.debit -= balance;
line.debit_str = self.formatCurrency(line.debit);
line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id);
} else {
line.credit -= balance;
line.credit_str = self.formatCurrency(line.credit);
line.credit_str = self.formatCurrency(line.credit, self.st_line.currency_id);
}
line.propose_partial_reconcile = false;
line.partial_reconcile = true;
@ -1291,12 +1294,13 @@ openerp.account = function (instance) {
},
unpartialReconcileLine: function(line) {
var self = this;
if (line.initial_amount > 0) {
line.debit = line.initial_amount;
line.debit_str = this.formatCurrency(line.debit);
line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id);
} else {
line.credit = -1 * line.initial_amount;
line.credit_str = this.formatCurrency(line.credit);
line.credit_str = self.formatCurrency(line.credit, self.st_line.currency_id);
}
line.propose_partial_reconcile = true;
line.partial_reconcile = false;
@ -1359,16 +1363,15 @@ openerp.account = function (instance) {
if (limit > 0) {
// Load move lines
deferred_move_lines = self.model_bank_statement_line
.call("get_move_lines_counterparts", [self.st_line.id, excluded_ids, self.filter, offset, limit])
.call("get_move_lines_counterparts_id", [self.st_line.id, excluded_ids, self.filter, offset, limit])
.then(function (lines) {
_(lines).each(self.decorateMoveLine.bind(self));
move_lines = lines;
});
}
// Fetch the number of move lines corresponding to this statement line and this filter
var deferred_total_move_lines_num = self.model_bank_statement_line
.call("get_move_lines_counterparts", [self.st_line.id, excluded_ids, self.filter, offset, limit, true])
.call("get_move_lines_counterparts_id", [self.st_line.id, excluded_ids, self.filter, 0, undefined, true])
.then(function(num){
move_lines_num = num;
});

View File

@ -185,7 +185,7 @@
<td><span class="toggle_create glyphicon glyphicon-play"></span></td>
<td><t t-esc="account_code"/></td>
<td></td>
<td>Open balance</td>
<td class="js_open_balance">Open balance</td>
<td><t t-esc="debit"/></td>
<td><t t-esc="credit"/></td>
<td></td>

View File

@ -99,7 +99,7 @@
</tr>
<tr t-foreach="get_lines_with_out_partner(data['form'])" t-as="not_partner">
<td>
<span t-esc="partner['name']"/>
<span t-esc="not_partner['name']"/>
</td>
<td class="text-right">
<span t-esc="formatLang(not_partner['direction'], currency_obj=res_company.currency_id)"/>

View File

@ -62,9 +62,10 @@ class account_chart(osv.osv_memory):
ORDER BY p.date_stop DESC
LIMIT 1) AS period_stop''', (fiscalyear_id, fiscalyear_id))
periods = [i[0] for i in cr.fetchall()]
if periods and len(periods) > 1:
if periods:
start_period = periods[0]
end_period = periods[1]
if len(periods) > 1:
end_period = periods[1]
res['value'] = {'period_from': start_period, 'period_to': end_period}
else:
res['value'] = {'period_from': False, 'period_to': False}

View File

@ -22,7 +22,6 @@ from dateutil.relativedelta import relativedelta
import datetime
import logging
import time
import traceback
from openerp.osv import osv, fields
from openerp.osv.orm import intersect, except_orm
@ -73,6 +72,7 @@ class account_analytic_invoice_line(osv.osv):
result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=local_context)
price = False
if price_unit is not False:
price = price_unit
elif pricelist_id:
@ -746,29 +746,32 @@ class account_analytic_account(osv.osv):
contract_ids = ids
else:
contract_ids = self.search(cr, uid, [('recurring_next_date','<=', current_date), ('state','=', 'open'), ('recurring_invoices','=', True), ('type', '=', 'contract')])
for contract in self.browse(cr, uid, contract_ids, context=context):
try:
invoice_values = self._prepare_invoice(cr, uid, contract, context=context)
invoice_ids.append(self.pool['account.invoice'].create(cr, uid, invoice_values, context=context))
next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d")
interval = contract.recurring_interval
if contract.recurring_rule_type == 'daily':
new_date = next_date+relativedelta(days=+interval)
elif contract.recurring_rule_type == 'weekly':
new_date = next_date+relativedelta(weeks=+interval)
elif contract.recurring_rule_type == 'monthly':
new_date = next_date+relativedelta(months=+interval)
else:
new_date = next_date+relativedelta(years=+interval)
self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context)
if automatic:
cr.commit()
except Exception:
if automatic:
cr.rollback()
_logger.error(traceback.format_exc())
else:
raise
if contract_ids:
cr.execute('SELECT company_id, array_agg(id) as ids FROM account_analytic_account WHERE id IN %s GROUP BY company_id', (tuple(contract_ids),))
for company_id, ids in cr.fetchall():
for contract in self.browse(cr, uid, ids, context=dict(context, company_id=company_id, force_company=company_id)):
try:
invoice_values = self._prepare_invoice(cr, uid, contract, context=context)
invoice_ids.append(self.pool['account.invoice'].create(cr, uid, invoice_values, context=context))
next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d")
interval = contract.recurring_interval
if contract.recurring_rule_type == 'daily':
new_date = next_date+relativedelta(days=+interval)
elif contract.recurring_rule_type == 'weekly':
new_date = next_date+relativedelta(weeks=+interval)
elif contract.recurring_rule_type == 'monthly':
new_date = next_date+relativedelta(months=+interval)
else:
new_date = next_date+relativedelta(years=+interval)
self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context)
if automatic:
cr.commit()
except Exception:
if automatic:
cr.rollback()
_logger.exception('Fail to create recurring invoice for contract %s', contract.code)
else:
raise
return invoice_ids
class account_analytic_account_summary_user(osv.osv):

View File

@ -248,7 +248,7 @@
<page string="Bill Information">
<field name="line_dr_ids" on_change="onchange_price(line_dr_ids, tax_id, partner_id)" context="{'journal_id':journal_id,'partner_id':partner_id}">
<tree string="Expense Lines" editable="bottom">
<field name="account_id" widget="selection" domain="[('user_type.report_type','=','expense'), ('type','!=','view')]"/>
<field name="account_id" domain="[('user_type.report_type','=','expense'), ('type','!=','view')]"/>
<field name="name"/>
<field name="amount"/>
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>

View File

@ -1,137 +1,41 @@
#
# Implements encrypting functions.
#
# Copyright (c) 2008, F S 3 Consulting Inc.
#
# Maintainer:
# Alec Joseph Rivera (agi<at>fs3.ph)
# refactored by Antony Lesuisse <al<at>openerp.com>
#
import hashlib
import hmac
import logging
from random import sample
from string import ascii_letters, digits
from passlib.context import CryptContext
import openerp
from openerp.osv import fields, osv
_logger = logging.getLogger(__name__)
magic_md5 = '$1$'
magic_sha256 = '$5$'
def gen_salt(length=8, symbols=None):
if symbols is None:
symbols = ascii_letters + digits
return ''.join(sample(symbols, length))
def md5crypt( raw_pw, salt, magic=magic_md5 ):
""" md5crypt FreeBSD crypt(3) based on but different from md5
The md5crypt is based on Mark Johnson's md5crypt.py, which in turn is
based on FreeBSD src/lib/libcrypt/crypt.c (1.2) by Poul-Henning Kamp.
Mark's port can be found in ActiveState ASPN Python Cookbook. Kudos to
Poul and Mark. -agi
Original license:
* "THE BEER-WARE LICENSE" (Revision 42):
*
* <phk@login.dknet.dk> wrote this file. As long as you retain this
* notice you can do whatever you want with this stuff. If we meet some
* day, and you think this stuff is worth it, you can buy me a beer in
* return.
*
* Poul-Henning Kamp
"""
raw_pw = raw_pw.encode('utf-8')
salt = salt.encode('utf-8')
hash = hashlib.md5()
hash.update( raw_pw + magic + salt )
st = hashlib.md5()
st.update( raw_pw + salt + raw_pw)
stretch = st.digest()
for i in range( 0, len( raw_pw ) ):
hash.update( stretch[i % 16] )
i = len( raw_pw )
while i:
if i & 1:
hash.update('\x00')
else:
hash.update( raw_pw[0] )
i >>= 1
saltedmd5 = hash.digest()
for i in range( 1000 ):
hash = hashlib.md5()
if i & 1:
hash.update( raw_pw )
else:
hash.update( saltedmd5 )
if i % 3:
hash.update( salt )
if i % 7:
hash.update( raw_pw )
if i & 1:
hash.update( saltedmd5 )
else:
hash.update( raw_pw )
saltedmd5 = hash.digest()
itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
rearranged = ''
for a, b, c in ((0, 6, 12), (1, 7, 13), (2, 8, 14), (3, 9, 15), (4, 10, 5)):
v = ord( saltedmd5[a] ) << 16 | ord( saltedmd5[b] ) << 8 | ord( saltedmd5[c] )
for i in range(4):
rearranged += itoa64[v & 0x3f]
v >>= 6
v = ord( saltedmd5[11] )
for i in range( 2 ):
rearranged += itoa64[v & 0x3f]
v >>= 6
return magic + salt + '$' + rearranged
def sh256crypt(cls, password, salt, magic=magic_sha256):
iterations = 1000
# see http://en.wikipedia.org/wiki/PBKDF2
result = password.encode('utf8')
for i in xrange(cls.iterations):
result = hmac.HMAC(result, salt, hashlib.sha256).digest() # uses HMAC (RFC 2104) to apply salt
result = result.encode('base64') # doesnt seem to be crypt(3) compatible
return '%s%s$%s' % (magic_sha256, salt, result)
default_crypt_context = CryptContext(
# kdf which can be verified by the context. The default encryption kdf is
# the first of the list
['pbkdf2_sha512', 'md5_crypt'],
# deprecated algorithms are still verified as usual, but ``needs_update``
# will indicate that the stored hash should be replaced by a more recent
# algorithm. Passlib 1.6 supports an `auto` value which deprecates any
# algorithm but the default, but Debian only provides 1.5 so...
deprecated=['md5_crypt'],
)
class res_users(osv.osv):
_inherit = "res.users"
def init(self, cr):
_logger.info("Hashing passwords, may be slow for databases with many users...")
cr.execute("SELECT id, password FROM res_users"
" WHERE password IS NOT NULL"
" AND password != ''")
for uid, pwd in cr.fetchall():
self._set_password(cr, openerp.SUPERUSER_ID, uid, pwd)
def set_pw(self, cr, uid, id, name, value, args, context):
if value:
encrypted = md5crypt(value, gen_salt())
cr.execute("update res_users set password='', password_crypt=%s where id=%s", (encrypted, id))
del value
self._set_password(cr, uid, id, value, context=context)
def get_pw( self, cr, uid, ids, name, args, context ):
cr.execute('select id, password from res_users where id in %s', (tuple(map(int, ids)),))
stored_pws = cr.fetchall()
res = {}
for id, stored_pw in stored_pws:
res[id] = stored_pw
return res
return dict(cr.fetchall())
_columns = {
'password': fields.function(get_pw, fnct_inv=set_pw, type='char', string='Password', invisible=True, store=True),
@ -141,27 +45,51 @@ class res_users(osv.osv):
def check_credentials(self, cr, uid, password):
# convert to base_crypt if needed
cr.execute('SELECT password, password_crypt FROM res_users WHERE id=%s AND active', (uid,))
encrypted = None
if cr.rowcount:
stored_password, stored_password_crypt = cr.fetchone()
if stored_password and not stored_password_crypt:
salt = gen_salt()
stored_password_crypt = md5crypt(stored_password, salt)
cr.execute("UPDATE res_users SET password='', password_crypt=%s WHERE id=%s", (stored_password_crypt, uid))
stored, encrypted = cr.fetchone()
if stored and not encrypted:
self._set_password(cr, uid, uid, stored)
try:
return super(res_users, self).check_credentials(cr, uid, password)
except openerp.exceptions.AccessDenied:
# check md5crypt
if stored_password_crypt:
if stored_password_crypt[:len(magic_md5)] == magic_md5:
salt = stored_password_crypt[len(magic_md5):11]
if stored_password_crypt == md5crypt(password, salt):
return
elif stored_password_crypt[:len(magic_md5)] == magic_sha256:
salt = stored_password_crypt[len(magic_md5):11]
if stored_password_crypt == md5crypt(password, salt):
return
# Reraise password incorrect
if encrypted:
valid_pass, replacement = self._crypt_context(cr, uid, uid)\
.verify_and_update(password, encrypted)
if replacement is not None:
self._set_encrypted_password(cr, uid, uid, replacement)
if valid_pass:
return
raise
def _set_password(self, cr, uid, id, password, context=None):
""" Encrypts then stores the provided plaintext password for the user
``id``
"""
encrypted = self._crypt_context(cr, uid, id, context=context).encrypt(password)
self._set_encrypted_password(cr, uid, id, encrypted, context=context)
def _set_encrypted_password(self, cr, uid, id, encrypted, context=None):
""" Store the provided encrypted password to the database, and clears
any plaintext password
:param uid: id of the current user
:param id: id of the user on which the password should be set
"""
cr.execute(
"UPDATE res_users SET password='', password_crypt=%s WHERE id=%s",
(encrypted, id))
def _crypt_context(self, cr, uid, id, context=None):
""" Passlib CryptContext instance used to encrypt and verify
passwords. Can be overridden if technical, legal or political matters
require different kdfs than the provided default.
Requires a CryptContext as deprecation and upgrade notices are used
internally
"""
return default_crypt_context
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -74,7 +74,7 @@ class OAuthLogin(Home):
state = dict(
d=request.session.db,
p=provider['id'],
r=redirect,
r=werkzeug.url_quote_plus(redirect),
)
token = request.params.get('token')
if token:
@ -143,7 +143,7 @@ class OAuthController(http.Controller):
cr.commit()
action = state.get('a')
menu = state.get('m')
redirect = state.get('r')
redirect = werkzeug.url_unquote_plus(state['r']) if state.get('r') else False
url = '/web'
if redirect:
url = redirect

View File

@ -102,7 +102,7 @@ openerp.calendar = function(instance) {
var self = this;
var action_url = '';
action_url = _.str.sprintf('/?db=%s#id=%s&view_type=form&model=calendar.event', db, meeting_id);
action_url = _.str.sprintf('/web?db=%s#id=%s&view_type=form&model=calendar.event', db, meeting_id);
var reload_page = function(){
return location.replace(action_url);

View File

@ -45,13 +45,10 @@ class crm_claim_stage(osv.osv):
help="Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams."),
'case_default': fields.boolean('Common to All Teams',
help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."),
'fold': fields.boolean('Hide in Views when Empty',
help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
}
_defaults = {
'sequence': lambda *args: 1,
'fold': False,
}
class crm_claim(osv.osv):

View File

@ -61,7 +61,6 @@
<field name="name">Rejected</field>
<field name="sequence">29</field>
<field name="case_default" eval="True"/>
<field name="fold" eval="True"/>
</record>

View File

@ -51,7 +51,6 @@
<field name="name"/>
<field name="case_default"/>
<field name="sequence"/>
<field name="fold"/>
</group>
</form>
</field>

View File

@ -32,7 +32,7 @@ class report_event_registration(osv.osv):
'draft_state': fields.integer(' # No of Draft Registrations', size=20),
'confirm_state': fields.integer(' # No of Confirmed Registrations', size=20),
'seats_max': fields.integer('Max Seats'),
'nbevent': fields.integer('Number Of Events'),
'nbevent': fields.integer('Number of Registrations'),
'event_type': fields.many2one('event.type', 'Event Type'),
'registration_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Attended'), ('cancel', 'Cancelled')], 'Registration State', readonly=True, required=True),
'event_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Event State', readonly=True, required=True),
@ -59,7 +59,7 @@ class report_event_registration(osv.osv):
r.name AS name_registration,
e.company_id AS company_id,
e.date_begin AS event_date,
count(e.id) AS nbevent,
count(r.id) AS nbevent,
CASE WHEN r.state IN ('draft') THEN r.nb_register ELSE 0 END AS draft_state,
CASE WHEN r.state IN ('open','done') THEN r.nb_register ELSE 0 END AS confirm_state,
e.type AS event_type,

View File

@ -1,7 +1,7 @@
.oe_event_date{
border-top-left-radius:3px;
border-top-right-radius:3px;
font-size: 48px;
font-size: 36px;
height: auto;
font-weight: bold;
text-align: center;

View File

@ -245,7 +245,8 @@ class event_ticket(osv.osv):
]
def onchange_product_id(self, cr, uid, ids, product_id=False, context=None):
return {'value': {'price': self.pool.get("product.product").browse(cr, uid, product_id).list_price or 0}}
price = self.pool.get("product.product").browse(cr, uid, product_id).list_price if product_id else 0
return {'value': {'price': price}}
class event_registration(osv.osv):

View File

@ -58,7 +58,7 @@ def start_end_date_for_period(period, default_start_date=False, default_end_date
end_date = default_end_date
if start_date and end_date:
return (start_date.strftime(DF), end_date.strftime(DF))
return (datetime.strftime(start_date, DF), datetime.strftime(end_date, DF))
else:
return (start_date, end_date)

View File

@ -699,7 +699,7 @@ class google_calendar(osv.AbstractModel):
for att in att_obj.browse(cr, uid, my_att_ids, context=context):
event = att.event_id
base_event_id = att.google_internal_event_id.split('_')[0]
base_event_id = att.google_internal_event_id.rsplit('_', 1)[0]
if base_event_id not in event_to_synchronize:
event_to_synchronize[base_event_id] = {}
@ -721,7 +721,7 @@ class google_calendar(osv.AbstractModel):
for event in all_event_from_google.values():
event_id = event.get('id')
base_event_id = event_id.split('_')[0]
base_event_id = event_id.rsplit('_', 1)[0]
if base_event_id not in event_to_synchronize:
event_to_synchronize[base_event_id] = {}
@ -786,7 +786,7 @@ class google_calendar(osv.AbstractModel):
if actSrc == 'OE':
self.delete_an_event(cr, uid, current_event[0], context=context)
elif actSrc == 'GG':
new_google_event_id = event.GG.event['id'].split('_')[1]
new_google_event_id = event.GG.event['id'].rsplit('_', 1)[1]
if 'T' in new_google_event_id:
new_google_event_id = new_google_event_id.replace('T', '')[:-1]
else:
@ -795,7 +795,8 @@ class google_calendar(osv.AbstractModel):
if event.GG.status:
parent_event = {}
if not event_to_synchronize[base_event][0][1].OE.event_id:
event_to_synchronize[base_event][0][1].OE.event_id = att_obj.search_read(cr, uid, [('google_internal_event_id', '=', event.GG.event['id'].split('_')[0])], ['event_id'], context=context_novirtual)[0].get('event_id')[0]
main_ev = att_obj.search_read(cr, uid, [('google_internal_event_id', '=', event.GG.event['id'].rsplit('_', 1)[0])], fields=['event_id'], context=context_novirtual)
event_to_synchronize[base_event][0][1].OE.event_id = main_ev[0].get('event_id')[0]
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
res = self.update_from_google(cr, uid, parent_event, event.GG.event, "copy", context)

View File

@ -225,7 +225,7 @@ class hr_employee(osv.osv):
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views."),
'image_small': fields.function(_get_image, fnct_inv=_set_image,
string="Smal-sized photo", type="binary", multi="_get_image",
string="Small-sized photo", type="binary", multi="_get_image",
store = {
'hr.employee': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},

View File

@ -40,7 +40,9 @@
<page string="Public Information">
<group>
<group string="Contact Information">
<field name="address_id" on_change="onchange_address_id(address_id)" context="{'show_address': 1}" options='{"always_reload": True, "highlight_first_line": True}'/>
<field name="address_id" on_change="onchange_address_id(address_id)"
context="{'show_address': 1, 'default_customer': False}"
options='{"always_reload": True, "highlight_first_line": True}'/>
<field name="mobile_phone"/>
<field name="work_location"/>
</group>
@ -68,7 +70,9 @@
<field name="otherid" groups="base.group_hr_user"/>
</group>
<group string="Contact Information">
<field name="address_home_id" context="{'show_address': 1}" options='{"always_reload": True, "highlight_first_line": True}'/>
<field name="address_home_id"
context="{'show_address': 1, 'default_customer': False}"
options='{"always_reload": True, "highlight_first_line": True}'/>
</group>
<group string="Status">
<field name="gender"/>

View File

@ -99,7 +99,7 @@ class hr_holidays_status(osv.osv):
for record in self.browse(cr, uid, ids, context=context):
name = record.name
if not record.limit:
name = name + (' (%d/%d)' % (record.leaves_taken or 0.0, record.max_leaves or 0.0))
name = name + (' (%g/%g)' % (record.leaves_taken or 0.0, record.max_leaves or 0.0))
res.append((record.id, name))
return res

View File

@ -59,25 +59,7 @@ A removal of one object in the CODA processing results in the removal of the
associated objects. The removal of a CODA File containing multiple Bank
Statements will also remove those associated statements.
The following reconciliation logic has been implemented in the CODA processing:
-------------------------------------------------------------------------------
1) The Company's Bank Account Number of the CODA statement is compared against
the Bank Account Number field of the Company's CODA Bank Account
configuration records (whereby bank accounts defined in type='info'
configuration records are ignored). If this is the case an 'internal transfer'
transaction is generated using the 'Internal Transfer Account' field of the
CODA File Import wizard.
2) As a second step the 'Structured Communication' field of the CODA transaction
line is matched against the reference field of in- and outgoing invoices
(supported : Belgian Structured Communication Type).
3) When the previous step doesn't find a match, the transaction counterparty is
located via the Bank Account Number configured on the OpenERP Customer and
Supplier records.
4) In case the previous steps are not successful, the transaction is generated
by using the 'Default Account for Unrecognized Movement' field of the CODA
File Import wizard in order to allow further manual processing.
In stead of a manual adjustment of the generated Bank Statements, you can also
Instead of a manual adjustment of the generated Bank Statements, you can also
re-import the CODA after updating the OpenERP database with the information that
was missing to allow automatic reconciliation.

View File

@ -28,46 +28,4 @@ class account_bank_statement(osv.osv):
}
class account_bank_statement_line(osv.osv):
_inherit = 'account.bank.statement.line'
_columns = {
'coda_account_number': fields.char('Account Number', help="The Counter Party Account Number")
}
def create(self, cr, uid, data, context=None):
"""
This function creates a Bank Account Number if, for a bank statement line,
the partner_id field and the coda_account_number field are set,
and the account number does not exist in the database
"""
if 'partner_id' in data and data['partner_id'] and 'coda_account_number' in data and data['coda_account_number']:
acc_number_ids = self.pool.get('res.partner.bank').search(cr, uid, [('acc_number', '=', data['coda_account_number'])])
if len(acc_number_ids) == 0:
try:
type_model, type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'bank_normal')
type_id = self.pool.get('res.partner.bank.type').browse(cr, uid, type_id, context=context)
self.pool.get('res.partner.bank').create(cr, uid, {'acc_number': data['coda_account_number'], 'partner_id': data['partner_id'], 'state': type_id.code}, context=context)
except ValueError:
pass
return super(account_bank_statement_line, self).create(cr, uid, data, context=context)
def write(self, cr, uid, ids, vals, context=None):
super(account_bank_statement_line, self).write(cr, uid, ids, vals, context)
"""
Same as create function above, but for write function
"""
if 'partner_id' in vals:
for line in self.pool.get('account.bank.statement.line').browse(cr, uid, ids, context=context):
if line.coda_account_number:
acc_number_ids = self.pool.get('res.partner.bank').search(cr, uid, [('acc_number', '=', line.coda_account_number)])
if len(acc_number_ids) == 0:
try:
type_model, type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'bank_normal')
type_id = self.pool.get('res.partner.bank.type').browse(cr, uid, type_id, context=context)
self.pool.get('res.partner.bank').create(cr, uid, {'acc_number': line.coda_account_number, 'partner_id': vals['partner_id'], 'state': type_id.code}, context=context)
except ValueError:
pass
return True
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -32,5 +32,27 @@
<field eval="'2011-01-31'" name="date_stop"/>
<field name="company_id" ref="base.main_company"/>
</record>
<!-- invoice with BBA -->
<record id="coda_demo_invoice_1" model="account.invoice">
<field name="currency_id" ref="base.EUR"/>
<field name="company_id" ref="base.main_company"/>
<field name="journal_id" ref="account.sales_journal"/>
<field name="period_id" ref="period_1_2011"/>
<field name="state">draft</field>
<field name="type">out_invoice</field>
<field name="account_id" ref="account.a_recv"/>
<field name="partner_id" ref="base.res_partner_9"/>
<field name="reference_type">bba</field>
<field name="reference">+++240/2838/42818+++</field>
</record>
<record id="invoice_1_line_1" model="account.invoice.line">
<field name="name">Otpez Laptop without OS</field>
<field name="invoice_id" ref="coda_demo_invoice_1"/>
<field name="price_unit">608.89</field>
<field name="quantity">10</field>
<field name="account_id" ref="account.a_sale"/>
</record>
<workflow action="invoice_open" model="account.invoice" ref="coda_demo_invoice_1"/>
</data>
</openerp>

View File

@ -4,7 +4,7 @@
2200010000 GKCCBEBB 1 0
2300010000BE41063012345610 PARTNER 1 0 1
3100010001OL44483FW SCTOFBIONLO001010001001PARTNER 1 0 0
2100020000OL4414AC8BOVSOVSOVERS00000000030444501101110015000002010237 11011113501 0
2100020000OL4414AC8BOVSOVSOVERS0000000003044450110111001500001101240283842818 11011113501 0
2200020000 BBRUBEBB 1 0
2300020000BE61310126985517 PARTNER 2 0 1
3100020001OL4414AC8BOVSOVSOVERS001500001001PARTNER 2 1 0

View File

@ -291,79 +291,38 @@ class account_coda_import(osv.osv_memory):
if 'counterpartyAddress' in line and line['counterpartyAddress'] != '':
note.append(_('Counter Party Address') + ': ' + line['counterpartyAddress'])
line['name'] = "\n".join(filter(None, [line['counterpartyName'], line['communication']]))
partner = None
partner_id = None
invoice = False
structured_com = ""
bank_account_id = False
if line['communication_struct'] and 'communication_type' in line and line['communication_type'] == '101':
ids = self.pool.get('account.invoice').search(cr, uid, [('reference', '=', line['communication']), ('reference_type', '=', 'bba')])
# Gère les communications structurées
# TODO : à faire primer sur resolution_proposition : si la communication indique une facture, on la sélectionne
# if ids:
# invoice = self.pool.get('account.invoice').browse(cr, uid, ids[0])
# partner = invoice.partner_id
# partner_id = partner.id
# if invoice.type in ['in_invoice', 'in_refund'] and line['debit'] == '1':
# line['transaction_type'] = 'supplier'
# elif invoice.type in ['out_invoice', 'out_refund'] and line['debit'] == '0':
# line['transaction_type'] = 'customer'
# line['account'] = invoice.account_id.id
# line['reconcile'] = False
# if invoice.type in ['in_invoice', 'out_invoice']:
# iml_ids = self.pool.get('account.move.line').search(cr, uid, [('move_id', '=', invoice.move_id.id), ('reconcile_id', '=', False), ('account_id.reconcile', '=', True)])
# if iml_ids:
# line['reconcile'] = iml_ids[0]
# if line['reconcile']:
# voucher_vals = {
# 'type': line['transaction_type'] == 'supplier' and 'payment' or 'receipt',
# 'name': line['name'],
# 'partner_id': partner_id,
# 'journal_id': statement['journal_id'].id,
# 'account_id': statement['journal_id'].default_credit_account_id.id,
# 'company_id': statement['journal_id'].company_id.id,
# 'currency_id': statement['journal_id'].company_id.currency_id.id,
# 'date': line['entryDate'],
# 'amount': abs(line['amount']),
# 'period_id': statement['period_id'],
# 'invoice_id': invoice.id,
# }
# context['invoice_id'] = invoice.id
# voucher_vals.update(self.pool.get('account.voucher').onchange_partner_id(cr, uid, [],
# partner_id=partner_id,
# journal_id=statement['journal_id'].id,
# amount=abs(line['amount']),
# currency_id=statement['journal_id'].company_id.currency_id.id,
# ttype=line['transaction_type'] == 'supplier' and 'payment' or 'receipt',
# date=line['transactionDate'],
# context=context
# )['value'])
# line_drs = []
# for line_dr in voucher_vals['line_dr_ids']:
# line_drs.append((0, 0, line_dr))
# voucher_vals['line_dr_ids'] = line_drs
# line_crs = []
# for line_cr in voucher_vals['line_cr_ids']:
# line_crs.append((0, 0, line_cr))
# voucher_vals['line_cr_ids'] = line_crs
# line['voucher_id'] = self.pool.get('account.voucher').create(cr, uid, voucher_vals, context=context)
structured_com = line['communication']
if 'counterpartyNumber' in line and line['counterpartyNumber']:
ids = self.pool.get('res.partner.bank').search(cr, uid, [('acc_number', '=', str(line['counterpartyNumber']))])
if ids and len(ids) > 0:
partner = self.pool.get('res.partner.bank').browse(cr, uid, ids[0], context=context).partner_id
partner_id = partner.id
if ids:
bank_account_id = ids[0]
partner_id = self.pool.get('res.partner.bank').browse(cr, uid, bank_account_id, context=context).partner_id.id
else:
#create the bank account, not linked to any partner. The reconciliation will link the partner manually
#chosen at the bank statement final confirmation time.
try:
type_model, type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'bank_normal')
type_id = self.pool.get('res.partner.bank.type').browse(cr, uid, type_id, context=context)
bank_code = type_id.code
except ValueError:
bank_code = 'bank'
bank_account_id = self.pool.get('res.partner.bank').create(cr, uid, {'acc_number': str(line['counterpartyNumber']), 'state': bank_code}, context=context)
if 'communication' in line and line['communication'] != '':
note.append(_('Communication') + ': ' + line['communication'])
data = {
'name': line['name'],
'note': "\n".join(note),
'note': "\n".join(note),
'date': line['entryDate'],
'amount': line['amount'],
'partner_id': partner_id,
'statement_id': statement['id'],
'ref': line['ref'],
'ref': structured_com,
'sequence': line['sequence'],
'coda_account_number': line['counterpartyNumber'],
'bank_account_id': bank_account_id,
}
self.pool.get('account.bank.statement.line').create(cr, uid, data, context=context)
if statement['coda_note'] != '':

View File

@ -141,7 +141,7 @@ class account_invoice(osv.osv):
elif algorithm == 'random':
if not self.check_bbacomm(reference):
base = random.randint(1, 9999999999)
bbacomm = str(base).rjust(7, '0')
bbacomm = str(base).rjust(10, '0')
base = int(bbacomm)
mod = base % 97 or 97
mod = str(mod).rjust(2, '0')

View File

@ -25,7 +25,7 @@
<record id="impuestos_plantilla_isv_por_cobrar" model="account.tax.template">
<field name="chart_template_id" ref="cuentas_plantilla"/>
<field name="name">ISV por Cobrar</field>
<field name="amount" eval="0.12"/>
<field name="amount" eval="0.15"/>
<field name="type">percent</field>
<field name="account_collected_id" ref="cta110301"/>
<field name="account_paid_id" ref="cta110301"/>
@ -42,7 +42,7 @@
<record id="impuestos_plantilla_isv_por_pagar" model="account.tax.template">
<field name="chart_template_id" ref="cuentas_plantilla"/>
<field name="name">ISV por Pagar</field>
<field name="amount" eval="0.12"/>
<field name="amount" eval="0.15"/>
<field name="type">percent</field>
<field name="account_collected_id" ref="cta210201"/>
<field name="account_paid_id" ref="cta210201"/>

View File

@ -32,7 +32,7 @@ This is the latest UK OpenERP localisation necessary to run OpenERP accounting f
- a few other adaptations""",
'author': 'SmartMode LTD',
'website': 'http://www.smartmode.co.uk',
'depends': ['base_iban', 'base_vat', 'account_chart'],
'depends': ['base_iban', 'base_vat', 'account_chart', 'account_anglo_saxon'],
'data': [
'data/account.account.type.csv',
'data/account.account.template.csv',

View File

@ -28,7 +28,7 @@ United States - Chart of accounts.
==================================
""",
'website': 'http://www.openerp.com',
'depends': ['account_chart'],
'depends': ['account_chart', 'account_anglo_saxon'],
'data': [
'l10n_us_account_type.xml',
'account_chart_template.xml',

View File

@ -176,7 +176,7 @@ class mail_notification(osv.Model):
references = message.parent_id.message_id if message.parent_id else False
# create email values
max_recipients = 100
max_recipients = 50
chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)]
email_ids = []
for chunk in chunks:
@ -188,7 +188,7 @@ class mail_notification(osv.Model):
'references': references,
}
email_ids.append(self.pool.get('mail.mail').create(cr, uid, mail_values, context=context))
if force_send and len(chunks) < 6: # for more than 500 followers, use the queue system
if force_send and len(chunks) < 2: # for more than 50 followers, use the queue system
self.pool.get('mail.mail').send(cr, uid, email_ids, context=context)
return True

View File

@ -211,3 +211,16 @@ class mail_group(osv.Model):
return []
else:
return super(mail_group, self).get_suggested_thread(cr, uid, removed_suggested_threads, context)
def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
res = super(mail_group, self).message_get_email_values(cr, uid, id, notif_mail=notif_mail, context=context)
group = self.browse(cr, uid, id, context=context)
res.update({
'headers': {
'Precedence': 'list',
}
})
if group.alias_domain:
res['headers']['List-Id'] = '%s.%s' % (group.alias_name, group.alias_domain)
res['headers']['List-Post'] = '<mailto:%s@%s>' % (group.alias_name, group.alias_domain)
return res

View File

@ -204,12 +204,15 @@ class mail_mail(osv.Model):
"""
body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context)
body_alternative = tools.html2plaintext(body)
return {
res = {
'body': body,
'body_alternative': body_alternative,
'subject': self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context),
'email_to': self.send_get_mail_to(cr, uid, mail, partner=partner, context=context),
}
if mail.model and mail.res_id and self.pool.get(mail.model) and hasattr(self.pool[mail.model], 'message_get_email_values'):
res.update(self.pool[mail.model].message_get_email_values(cr, uid, mail.res_id, mail, context=context))
return res
def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None):
""" Sends the selected emails immediately, ignoring their current
@ -268,6 +271,9 @@ class mail_mail(osv.Model):
# build an RFC2822 email.message.Message object and send it without queuing
res = None
for email in email_list:
email_headers = dict(headers)
if email.get('headers'):
email_headers.update(email['headers'])
msg = ir_mail_server.build_email(
email_from=mail.email_from,
email_to=email.get('email_to'),
@ -282,7 +288,7 @@ class mail_mail(osv.Model):
object_id=mail.res_id and ('%s-%s' % (mail.res_id, mail.model)),
subtype='html',
subtype_alternative='plain',
headers=headers)
headers=email_headers)
res = ir_mail_server.send_email(cr, uid, msg,
mail_server_id=mail.mail_server_id.id,
context=context)

View File

@ -34,6 +34,7 @@ import pytz
import socket
import time
import xmlrpclib
import re
from email.message import Message
from urllib import urlencode
@ -48,6 +49,8 @@ from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
mail_header_msgid_re = re.compile('<[^<>]+>')
def decode_header(message, header, separator=' '):
return separator.join(map(decode, filter(None, message.get_all(header, []))))
@ -694,6 +697,16 @@ class mail_thread(osv.AbstractModel):
if record.alias_domain and record.alias_name else False
for record in self.browse(cr, SUPERUSER_ID, ids, context=context)]
def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
""" Temporary method to create custom notification email values for a given
model and document. This should be better to have a headers field on
the mail.mail model, computed when creating the notification email, but
this cannot be done in a stable version.
TDE FIXME: rethink this ulgy thing. """
res = dict()
return res
#------------------------------------------------------
# Mail gateway
#------------------------------------------------------
@ -1301,13 +1314,13 @@ class mail_thread(osv.AbstractModel):
msg_dict['date'] = stored_date.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
if message.get('In-Reply-To'):
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', '=', decode(message['In-Reply-To']))])
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', '=', decode(message['In-Reply-To'].strip()))])
if parent_ids:
msg_dict['parent_id'] = parent_ids[0]
if message.get('References') and 'parent_id' not in msg_dict:
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', 'in',
[x.strip() for x in decode(message['References']).split()])])
msg_list = mail_header_msgid_re.findall(decode(message['References']))
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', 'in', [x.strip() for x in msg_list])])
if parent_ids:
msg_dict['parent_id'] = parent_ids[0]

View File

@ -267,10 +267,7 @@ class mail_compose_message(osv.TransientModel):
# mass mailing: rendering override wizard static values
if mass_mail_mode and wizard.model:
# always keep a copy, reset record name (avoid browsing records)
mail_values.update(notification=True, record_name=False)
if hasattr(self.pool[wizard.model], 'message_new'):
mail_values['model'] = wizard.model
mail_values['res_id'] = res_id
mail_values.update(notification=True, model=wizard.model, res_id=res_id, record_name=False)
# auto deletion of mail_mail
if 'mail_auto_delete' in context:
mail_values['auto_delete'] = context.get('mail_auto_delete')

View File

@ -84,7 +84,8 @@ class MailMail(osv.Model):
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None):
res = super(MailMail, self).send_get_email_dict(cr, uid, mail, partner, context=context)
if mail.mailing_id and res.get('body') and res.get('email_to'):
email_to = tools.email_split(res.get('email_to')[0])
emails = tools.email_split(res.get('email_to')[0])
email_to = emails and emails[0] or False
unsubscribe_url = self._get_unsubscribe_url(cr, uid, mail, email_to, context=context)
if unsubscribe_url:
res['body'] = tools.append_content_to_html(res['body'], unsubscribe_url, plaintext=False, container_tag='p')

View File

@ -591,6 +591,7 @@
<field name="mail_mail_id"/>
<field name="message_id"/>
<field name="sent"/>
<field name="exception"/>
<field name="opened"/>
<field name="replied"/>
<field name="bounced"/>

View File

@ -1071,8 +1071,8 @@ class mrp_production(osv.osv):
return False
# Take routing location as a Source Location.
source_location_id = production.location_src_id.id
if production.bom_id.routing_id and production.bom_id.routing_id.location_id:
source_location_id = production.bom_id.routing_id.location_id.id
if production.routing_id and production.routing_id.location_id:
source_location_id = production.routing_id.location_id.id
destination_location_id = production.product_id.property_stock_production.id
if not source_location_id:

View File

@ -678,6 +678,17 @@ class product_template(osv.osv):
if not context or "create_product_product" not in context:
self.create_variant_ids(cr, uid, [product_template_id], context=context)
self._set_standard_price(cr, uid, product_template_id, vals.get('standard_price', 0.0), context=context)
# TODO: this is needed to set given values to first variant after creation
# these fields should be moved to product as lead to confusion
related_vals = {}
if vals.get('ean13'):
related_vals['ean13'] = vals['ean13']
if vals.get('default_code'):
related_vals['default_code'] = vals['default_code']
if related_vals:
self.write(cr, uid, product_template_id, related_vals, context=context)
return product_template_id
def write(self, cr, uid, ids, vals, context=None):

View File

@ -27,7 +27,7 @@ class account_invoice(osv.Model):
template_values = Composer.onchange_template_id(
cr, uid, composer_id, line.product_id.email_template_id.id, 'comment', 'account.invoice', invoice.id
)['value']
template_values['attachment_ids'] = [(4, id) for id in template_values.get('attachment_ids', '[]')]
template_values['attachment_ids'] = [(4, id) for id in template_values.get('attachment_ids', [])]
Composer.write(cr, uid, [composer_id], template_values, context=context)
Composer.send_mail(cr, uid, [composer_id], context=context)
return True

View File

@ -50,8 +50,8 @@
<field name="user_id" ref="base.user_demo"/>
<field name="alias_model">project.task</field>
<field name="message_follower_ids" eval="[(6, 0, [
ref('base.user_root'),
ref('base.user_demo')])]"/>
ref('base.partner_root'),
ref('base.partner_demo')])]"/>
</record>
<!-- We assign after so that default values applies -->

View File

@ -27,11 +27,11 @@ class project_configuration(osv.osv_memory):
_inherit = 'res.config.settings'
_columns = {
'module_project_mrp': fields.boolean('Generate tasks from sale orders',
'module_sale_service': fields.boolean('Generate tasks from sale orders',
help='This feature automatically creates project tasks from service products in sale orders. '
'More precisely, tasks are created for procurement lines with product of type \'Service\', '
'procurement method \'Make to Order\', and supply method \'Manufacture\'.\n'
'-This installs the module project_mrp.'),
'-This installs the module sale_service.'),
'module_pad': fields.boolean("Use integrated collaborative note pads on task",
help='Lets the company customize which Pad installation should be used to link to new pads '
'(for example: http://ietherpad.com/).\n'

View File

@ -33,8 +33,8 @@
<label for="module_project_timesheet"/>
</div>
<div>
<field name="module_project_mrp" class="oe_inline"/>
<label for="module_project_mrp"/>
<field name="module_sale_service" class="oe_inline"/>
<label for="module_sale_service"/>
</div>
<div>
<field name="module_pad" class="oe_inline"/>

View File

@ -137,7 +137,7 @@ class project_work(osv.osv):
amount = vals_line['unit_amount']
prod_id = vals_line['product_id']
unit = False
timeline_id = timesheet_obj.create(cr, uid, vals=vals_line, context=context)
timeline_id = timesheet_obj.create(cr, uid, vals_line, context=context)
# Compute based on pricetype
amount_unit = timesheet_obj.on_change_unit_amount(cr, uid, timeline_id,

View File

@ -1310,6 +1310,7 @@ class procurement_order(osv.osv):
if available_draft_po_ids:
po_id = available_draft_po_ids[0]
po_rec = po_obj.browse(cr, uid, po_id, context=context)
#if the product has to be ordered earlier those in the existing PO, we replace the purchase date on the order to avoid ordering it too late
if datetime.strptime(po_rec.date_order, DEFAULT_SERVER_DATE_FORMAT) > purchase_date:
po_obj.write(cr, uid, [po_id], {'date_order': purchase_date}, context=context)
#look for any other PO line in the selected PO with same product and UoM to sum quantities instead of creating a new po line
@ -1369,8 +1370,11 @@ class product_template(osv.Model):
_inherit = 'product.template'
def _get_buy_route(self, cr, uid, context=None):
buy_route = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'purchase', 'route_warehouse0_buy')[1]
return [buy_route]
buy_route = self.pool.get('ir.model.data').xmlid_to_res_id(cr, uid, 'purchase.route_warehouse0_buy')
if buy_route:
return [buy_route]
return []
def _purchase_count(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, 0)

View File

@ -79,9 +79,7 @@ class stock_move(osv.osv):
if move.purchase_line_id:
purchase_line = move.purchase_line_id
res['invoice_line_tax_id'] = [(6, 0, [x.id for x in purchase_line.taxes_id])]
#res['account_analytic_id'] = purc_line.order_id.project_id and sale_line.order_id.project_id.id or False
res['price_unit'] = purchase_line.price_unit
#res['discount'] = sale_line.discount
return res
class stock_picking(osv.osv):

View File

@ -134,14 +134,15 @@ class Report(osv.Model):
website = None
if request and hasattr(request, 'website'):
website = request.website
values.update({
'time': time,
'translate_doc': translate_doc,
'editable': True, # Will active inherit_branding
'user': user,
'res_company': user.company_id,
'website': website,
})
values.update(
time=time,
translate_doc=translate_doc,
editable=True, # Will active inherit_branding
user=user,
res_company=user.company_id,
website=website,
editable_no_editor=True,
)
return view_obj.render(cr, uid, template, values, context=context)
#--------------------------------------------------------------------------

View File

@ -122,7 +122,7 @@ Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
def onchange_task_work(self, cr, uid, ids, task_work, context=None):
return {'value': {
'module_project_timesheet': task_work,
'module_project_mrp': task_work,
'module_sale_service': task_work,
}}
def onchange_timesheet(self, cr, uid, ids, timesheet, context=None):

View File

@ -128,13 +128,12 @@ class sale_order(osv.osv):
sale_clause = ''
no_invoiced = False
for arg in args:
if arg[1] == '=':
if arg[2]:
clause += 'AND inv.state = \'paid\''
else:
clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND rel.order_id = sale.id '
sale_clause = ', sale_order AS sale '
no_invoiced = True
if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
clause += 'AND inv.state = \'paid\''
else:
clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND rel.order_id = sale.id '
sale_clause = ', sale_order AS sale '
no_invoiced = True
cursor.execute('SELECT rel.order_id ' \
'FROM sale_order_invoice_rel AS rel, account_invoice AS inv '+ sale_clause + \
@ -686,7 +685,7 @@ class sale_order(osv.osv):
def procurement_needed(self, cr, uid, ids, context=None):
#when sale is installed only, there is no need to create procurements, that's only
#further installed modules (project_mrp, sale_stock) that will change this.
#further installed modules (sale_service, sale_stock) that will change this.
sale_line_obj = self.pool.get('sale.order.line')
res = []
for order in self.browse(cr, uid, ids, context=context):
@ -839,7 +838,7 @@ class sale_order_line(osv.osv):
def need_procurement(self, cr, uid, ids, context=None):
#when sale is installed only, there is no need to create procurements, that's only
#further installed modules (project_mrp, sale_stock) that will change this.
#further installed modules (sale_service, sale_stock) that will change this.
return False
def _amount_line(self, cr, uid, ids, field_name, arg, context=None):

View File

@ -19,6 +19,6 @@
#
##############################################################################
import project_mrp
import models
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -50,8 +50,8 @@ completed.
'website': 'http://www.openerp.com',
'images': ['images/product.jpeg', 'images/task_from_SO.jpeg'],
'depends': ['project', 'procurement', 'sale', 'procurement_jit'],
'data': ['project_mrp_view.xml'], #'process/project_mrp_process.xml'
'demo': ['project_mrp_demo.xml'],
'data': ['views/sale_service_view.xml'],
'demo': ['demo/sale_service_demo.xml'],
'test': ['test/project_task_procurement.yml'],
'installable': True,
'auto_install': False,

Some files were not shown because too many files have changed in this diff Show More