Merge remote-tracking branch 'odoo/master' into master-wmsstagingmig-jco
This commit is contained in:
commit
cf48492570
|
@ -1,2 +0,0 @@
|
|||
.*
|
||||
**/node_modules
|
|
@ -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),
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)"/>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
<field name="name"/>
|
||||
<field name="case_default"/>
|
||||
<field name="sequence"/>
|
||||
<field name="fold"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'] != '':
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import project_mrp
|
||||
import models
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -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
Loading…
Reference in New Issue