'account.mt_invoice_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'open' and obj.type in ('out_invoice', 'out_refund'),
},
}
- _columns = {
- 'name': fields.char('Reference/Description', size=64, select=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'origin': fields.char('Source Document', size=64, help="Reference of the document that produced this invoice.", readonly=True, states={'draft':[('readonly',False)]}),
- 'supplier_invoice_number': fields.char('Supplier Invoice Number', size=64, help="The reference of this invoice as provided by the supplier.", readonly=True, states={'draft':[('readonly',False)]}),
- 'type': fields.selection([
+
+ @api.one
+ @api.depends('invoice_line.price_subtotal', 'tax_line.amount')
+ def _compute_amount(self):
+ self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line)
+ self.amount_tax = sum(line.amount for line in self.tax_line)
+ self.amount_total = self.amount_untaxed + self.amount_tax
+
+ @api.model
+ def _default_journal(self):
+ inv_type = self._context.get('type', 'out_invoice')
+ inv_types = inv_type if isinstance(inv_type, list) else [inv_type]
+ company_id = self._context.get('company_id', self.env.user.company_id.id)
+ domain = [
+ ('type', 'in', filter(None, map(TYPE2JOURNAL.get, inv_types))),
+ ('company_id', '=', company_id),
+ ]
+ return self.env['account.journal'].search(domain, limit=1)
+
+ @api.model
+ def _default_currency(self):
+ journal = self._default_journal()
+ return journal.currency or journal.company_id.currency_id
+
+ @api.model
+ @api.returns('account.analytic.journal')
+ def _get_journal_analytic(self, inv_type):
+ """ Return the analytic journal corresponding to the given invoice type. """
+ journal_type = TYPE2JOURNAL.get(inv_type, 'sale')
+ journal = self.env['account.analytic.journal'].search([('type', '=', journal_type)], limit=1)
+ if not journal:
+ raise except_orm(_('No Analytic Journal!'),
+ _("You must define an analytic journal of type '%s'!") % (journal_type,))
+ return journal
+
+ @api.one
+ @api.depends('account_id', 'move_id.line_id.account_id', 'move_id.line_id.reconcile_id')
+ def _compute_reconciled(self):
+ self.reconciled = self.test_paid()
+
+ @api.model
+ def _get_reference_type(self):
+ return [('none', _('Free Reference'))]
+
+ @api.one
+ @api.depends(
+ 'state', 'currency_id', 'invoice_line.price_subtotal',
+ 'move_id.line_id.account_id.type',
+ 'move_id.line_id.amount_residual',
+ 'move_id.line_id.amount_residual_currency',
+ 'move_id.line_id.currency_id',
+ 'move_id.line_id.reconcile_partial_id.line_partial_ids.invoice.type',
+ )
+ def _compute_residual(self):
+ nb_inv_in_partial_rec = max_invoice_id = 0
+ self.residual = 0.0
+ for line in self.move_id.line_id:
+ if line.account_id.type in ('receivable', 'payable'):
+ if line.currency_id == self.currency_id:
+ self.residual += line.amount_residual_currency
+ else:
+ # ahem, shouldn't we use line.currency_id here?
+ from_currency = line.company_id.currency_id.with_context(date=line.date)
+ self.residual += from_currency.compute(line.amount_residual, self.currency_id)
+ # we check if the invoice is partially reconciled and if there
+ # are other invoices involved in this partial reconciliation
+ for pline in line.reconcile_partial_id.line_partial_ids:
+ if pline.invoice and self.type == pline.invoice.type:
+ nb_inv_in_partial_rec += 1
+ # store the max invoice id as for this invoice we will
+ # make a balance instead of a simple division
+ max_invoice_id = max(max_invoice_id, pline.invoice.id)
+ if nb_inv_in_partial_rec:
+ # if there are several invoices in a partial reconciliation, we
+ # split the residual by the number of invoices to have a sum of
+ # residual amounts that matches the partner balance
+ new_value = self.currency_id.round(self.residual / nb_inv_in_partial_rec)
+ if self.id == max_invoice_id:
+ # if it's the last the invoice of the bunch of invoices
+ # partially reconciled together, we make a balance to avoid
+ # rounding errors
+ self.residual = self.residual - ((nb_inv_in_partial_rec - 1) * new_value)
+ else:
+ self.residual = new_value
+ # prevent the residual amount on the invoice to be less than 0
+ self.residual = max(self.residual, 0.0)
+
+ @api.one
+ @api.depends(
+ 'move_id.line_id.account_id',
+ 'move_id.line_id.reconcile_id.line_id',
+ 'move_id.line_id.reconcile_partial_id.line_partial_ids',
+ )
+ def _compute_move_lines(self):
+ # Give Journal Items related to the payment reconciled to this invoice.
+ # Return partial and total payments related to the selected invoice.
+ self.move_lines = self.env['account.move.line']
+ if not self.move_id:
+ return
+ data_lines = self.move_id.line_id.filtered(lambda l: l.account_id == self.account_id)
+ partial_lines = self.env['account.move.line']
+ for data_line in data_lines:
+ if data_line.reconcile_id:
+ lines = data_line.reconcile_id.line_id
+ elif data_line.reconcile_partial_id:
+ lines = data_line.reconcile_partial_id.line_partial_ids
+ else:
+ lines = self.env['account_move_line']
+ partial_lines += data_line
+ self.move_lines = lines - partial_lines
+
+ @api.one
+ @api.depends(
+ 'move_id.line_id.reconcile_id.line_id',
+ 'move_id.line_id.reconcile_partial_id.line_partial_ids',
+ )
+ def _compute_payments(self):
+ partial_lines = lines = self.env['account.move.line']
+ for line in self.move_id.line_id:
++ if line.account_id != self.account_id:
++ continue
+ if line.reconcile_id:
+ lines |= line.reconcile_id.line_id
+ elif line.reconcile_partial_id:
+ lines |= line.reconcile_partial_id.line_partial_ids
+ partial_lines += line
+ self.payment_ids = (lines - partial_lines).sorted()
+
+ name = fields.Char(string='Reference/Description', index=True,
+ readonly=True, states={'draft': [('readonly', False)]})
+ origin = fields.Char(string='Source Document',
+ help="Reference of the document that produced this invoice.",
+ readonly=True, states={'draft': [('readonly', False)]})
+ supplier_invoice_number = fields.Char(string='Supplier Invoice Number',
+ help="The reference of this invoice as provided by the supplier.",
+ readonly=True, states={'draft': [('readonly', False)]})
+ type = fields.Selection([
('out_invoice','Customer Invoice'),
('in_invoice','Supplier Invoice'),
('out_refund','Customer Refund'),
<field name="arch" type="xml">
<search string="Invoices Analysis">
<field name="date"/>
- <filter icon="terp-go-year" string="Year" name="year" domain="[('date','<=', time.strftime('%%Y-%%m-%%d')),('date','>=',time.strftime('%%Y-01-01'))]" help="year"/>
+ <filter string="This Year" name="year" domain="['|', ('date', '=', False), '&',('date','<=', time.strftime('%%Y-12-31')),('date','>=',time.strftime('%%Y-01-01'))]"/>
<separator/>
- <filter string="Draft" icon="terp-document-new" domain="[('state','=','draft')]" help = "Draft Invoices"/>
- <filter string="Pro-forma" icon="terp-gtk-media-pause" domain="['|', ('state','=','proforma'),('state','=','proforma2')]" help = "Pro-forma Invoices"/>
- <filter string="Invoiced" name="current" icon="terp-check" domain="[('state','not in', ('draft','cancel','proforma','proforma2'))]" help = "Open and Paid Invoices"/>
+ <filter string="To Invoice" domain="[('state','=','draft')]" help = "Draft Invoices"/>
+ <filter string="Pro-forma" domain="['|', ('state','=','proforma'),('state','=','proforma2')]"/>
+ <filter string="Invoiced" name="current" domain="[('state','not in', ('draft','cancel','proforma','proforma2'))]"/>
<separator/>
- <filter icon="terp-personal" string="Customer" name="customer" domain="['|', ('type','=','out_invoice'),('type','=','out_refund')]" help="Customer Invoices And Refunds"/>
- <filter icon="terp-personal" string="Supplier" domain="['|', ('type','=','in_invoice'),('type','=','in_refund')]" help="Supplier Invoices And Refunds"/>
+ <filter string="Customer" name="customer" domain="['|', ('type','=','out_invoice'),('type','=','out_refund')]"/>
+ <filter string="Supplier" domain="['|', ('type','=','in_invoice'),('type','=','in_refund')]"/>
<separator/>
- <filter icon="terp-dolar" string="Invoice" domain="['|', ('type','=','out_invoice'),('type','=','in_invoice')]" help="Customer And Supplier Invoices"/>
- <filter icon="terp-dolar_ok!" string="Refund" domain="['|', ('type','=','out_refund'),('type','=','in_refund')]" help="Customer And Supplier Refunds"/>
+ <filter string="Invoice" domain="['|', ('type','=','out_invoice'),('type','=','in_invoice')]"/>
+ <filter string="Refund" domain="['|', ('type','=','out_refund'),('type','=','in_refund')]"/>
- <field name="partner_id"/>
+ <field name="partner_id" operator="child_of"/>
<field name="user_id" />
<field name="categ_id" filter_domain="[('categ_id', 'child_of', self)]"/>
- <group expand="1" string="Group By...">
+ <group expand="1" string="Group By">
<filter string="Partner" name="partner_id" context="{'group_by':'partner_id','residual_visible':True}"/>
<filter string="Commercial Partner" name="commercial_partner_id" context="{'group_by':'commercial_partner_id','residual_visible':True}"/>
<filter string="Commercial Partner's Country" name="country_id" context="{'group_by':'country_id'}"/>
'categ_ids': fields.many2many('calendar.event.type', 'meeting_category_rel', 'event_id', 'type_id', 'Tags'),
'attendee_ids': fields.one2many('calendar.attendee', 'event_id', 'Attendees', ondelete='cascade'),
'partner_ids': fields.many2many('res.partner', 'calendar_event_res_partner_rel', string='Attendees', states={'done': [('readonly', True)]}),
- 'alarm_ids': fields.many2many('calendar.alarm', 'calendar_alarm_calendar_event_rel', string='Reminders', ondelete="restrict"),
+ 'alarm_ids': fields.many2many('calendar.alarm', 'calendar_alarm_calendar_event_rel', string='Reminders', ondelete="restrict", copy=False),
}
+
+ def _get_default_partners(self, cr, uid, ctx=None):
+ ret = [self.pool['res.users'].browse(cr, uid, uid, context=ctx).partner_id.id]
+ active_id = ctx.get('active_id')
+ if ctx.get('active_model') == 'res.partner' and active_id:
+ if active_id not in ret:
+ ret.append(active_id)
+ return ret
+
_defaults = {
'end_type': 'count',
'count': 1,
help="Message subtypes followed, meaning subtypes that will be pushed onto the user's Wall."),
}
+ #
+ # Modifying followers change access rights to individual documents. As the
+ # cache may contain accessible/inaccessible data, one has to refresh it.
+ #
+ def create(self, cr, uid, vals, context=None):
+ res = super(mail_followers, self).create(cr, uid, vals, context=context)
+ self.invalidate_cache(cr, uid, context=context)
+ return res
+
+ def write(self, cr, uid, ids, vals, context=None):
+ res = super(mail_followers, self).write(cr, uid, ids, vals, context=context)
+ self.invalidate_cache(cr, uid, context=context)
+ return res
+
+ def unlink(self, cr, uid, ids, context=None):
+ res = super(mail_followers, self).unlink(cr, uid, ids, context=context)
+ self.invalidate_cache(cr, uid, context=context)
+ return res
+
+ _sql_constraints = [('mail_followers_res_partner_res_model_id_uniq','unique(res_model,res_id,partner_id)','Error, a partner cannot follow twice the same object.')]
class mail_notification(osv.Model):
""" Class holding notifications pushed to partners. Followers and partners
<br />
<t t-esc="widget.pos.company.name"/><br />
Phone: <t t-esc="widget.pos.company.phone || ''"/><br />
- User: <t t-esc="widget.pos.user.name"/><br />
+ User: <t t-esc="widget.pos.cashier.name"/><br />
+ Shop: <t t-esc="widget.pos.shop.name"/><br />
<br />
<t t-if="widget.pos.config.receipt_header">
<div style='text-align:center'>
import psycopg2
from datetime import datetime
from dateutil.relativedelta import relativedelta
+ import pytz
import openerp
++<<<<<<< HEAD
+from openerp import SUPERUSER_ID, netsvc, api
++=======
+ from openerp import netsvc, SUPERUSER_ID
++>>>>>>> 0739bc4edabab7e74571087c01a2da68ccadb10e
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval as eval
must not be committed/rolled back!
"""
try:
- now = fields.datetime.context_timestamp(job_cr, SUPERUSER_ID, datetime.now())
- nextcall = fields.datetime.context_timestamp(job_cr, SUPERUSER_ID, datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT))
- numbercall = job['numbercall']
-
- ok = False
- while nextcall < now and numbercall:
- if numbercall > 0:
- numbercall -= 1
- if not ok or job['doall']:
- self._callback(job_cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
- if numbercall:
- nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
- ok = True
- addsql = ''
- if not numbercall:
- addsql = ', active=False'
- cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
- (nextcall.astimezone(pytz.UTC).strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
+ with api.Environment.manage():
- now = datetime.now()
- nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
++ now = fields.datetime.context_timestamp(job_cr, SUPERUSER_ID, datetime.now())
++ nextcall = fields.datetime.context_timestamp(job_cr, SUPERUSER_ID, datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT))
+ numbercall = job['numbercall']
+
+ ok = False
+ while nextcall < now and numbercall:
+ if numbercall > 0:
+ numbercall -= 1
+ if not ok or job['doall']:
+ self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
+ if numbercall:
+ nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
+ ok = True
+ addsql = ''
+ if not numbercall:
+ addsql = ', active=False'
+ cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
- (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
++ (nextcall.astimezone(pytz.UTC).strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
+ self.invalidate_cache(cr, SUPERUSER_ID)
finally:
- job_cr.commit()
+ cr.commit()
cron_cr.commit()
@classmethod
if not el.attrib.get('on_change', False):
continue
- match = re.match("([a-z_1-9A-Z]+)\((.*)\)", el.attrib['on_change'])
- assert match, "Unable to parse the on_change '%s'!" % (el.attrib['on_change'], )
-
- # creating the context
- class parent2(object):
- def __init__(self, d):
- self.d = d
- def __getattr__(self, name):
- return self.d.get(name, False)
-
- ctx = record_dict.copy()
- ctx['context'] = self.context
- ctx['uid'] = SUPERUSER_ID
- ctx['parent'] = parent2(parent)
- for a in fg:
- if a not in ctx:
- ctx[a] = process_val(a, defaults.get(a, False))
-
- # Evaluation args
- args = map(lambda x: eval(x, ctx), match.group(2).split(','))
- result = getattr(model, match.group(1))(self.cr, SUPERUSER_ID, [], *args)
+
+ if el.attrib['on_change'] in ('1', 'true'):
+ # New-style on_change
+ recs = model.browse(self.cr, SUPERUSER_ID, [], self.context)
+ result = recs.onchange(record_dict, field_name, onchange_spec)
+
+ else:
+ match = re.match("([a-z_1-9A-Z]+)\((.*)\)", el.attrib['on_change'])
+ assert match, "Unable to parse the on_change '%s'!" % (el.attrib['on_change'], )
+
+ # creating the context
+ class parent2(object):
+ def __init__(self, d):
+ self.d = d
+ def __getattr__(self, name):
+ return self.d.get(name, False)
+
+ ctx = record_dict.copy()
+ ctx['context'] = self.context
+ ctx['uid'] = SUPERUSER_ID
+ ctx['parent'] = parent2(parent)
+ for a in fg:
+ if a not in ctx:
+ ctx[a] = process_val(a, defaults.get(a, False))
+
+ # Evaluation args
+ args = map(lambda x: eval(x, ctx), match.group(2).split(','))
+ result = getattr(model, match.group(1))(self.cr, SUPERUSER_ID, [], *args)
+
for key, val in (result or {}).get('value', {}).items():
- assert key in fg, (
- "The field %r returned from the onchange call %r "
- "does not exist in the source view %r (of object "
- "%r). This field will be ignored (and thus not "
- "populated) when clients saves the new record" % (
- key, match.group(1), view_info.get('name', '?'), model._name
- ))
- if key not in fields:
- # do not shadow values explicitly set in yaml.
- record_dict[key] = process_val(key, val)
+ if key in fg:
+ if key not in fields:
+ # do not shadow values explicitly set in yaml.
+ record_dict[key] = process_val(key, val)
+ else:
+ _logger.debug("The returning field '%s' from your on_change call '%s'"
+ " does not exist either on the object '%s', either in"
+ " the view '%s'",
+ key, match.group(1), model._name, view_info['name'])
else:
nodes = list(el) + nodes
else: