[MERGE] forward port of branch 8.0 up to 33a8989
This commit is contained in:
+2
-1
@@ -4,6 +4,7 @@ _build/
|
||||
# dotfiles
|
||||
.*
|
||||
!.gitignore
|
||||
!.mailmap
|
||||
# compiled python files
|
||||
*.py[co]
|
||||
# setup.py egg_info
|
||||
@@ -30,4 +31,4 @@ install/win32/meta.py
|
||||
/lib/
|
||||
/man/
|
||||
/share/
|
||||
/src/
|
||||
/src/
|
||||
|
||||
@@ -1370,9 +1370,6 @@ class account_move(osv.osv):
|
||||
self.validate(cr, uid, ids, context=context)
|
||||
return result
|
||||
|
||||
#
|
||||
# TODO: Check if period is closed !
|
||||
#
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
context = dict(context or {})
|
||||
if vals.get('line_id'):
|
||||
@@ -1534,6 +1531,8 @@ class account_move(osv.osv):
|
||||
line_ids = []
|
||||
line_draft_ids = []
|
||||
company_id = None
|
||||
# makes sure we don't use outdated period
|
||||
obj_move_line._update_journal_check(cr, uid, journal.id, move.period_id.id, context=context)
|
||||
for line in move.line_id:
|
||||
amount += line.debit - line.credit
|
||||
line_ids.append(line.id)
|
||||
|
||||
@@ -1221,7 +1221,7 @@ class account_move_line(osv.osv):
|
||||
period = period_obj.browse(cr, uid, period_id, context=context)
|
||||
for (state,) in result:
|
||||
if state == 'done':
|
||||
raise UserError(_('You can not add/modify entries in a closed period %s of journal %s.' % (period.name,journal.name)))
|
||||
raise UserError(_('You can not add/modify entries in a closed period %s of journal %s.') % (period.name,journal.name))
|
||||
if not result:
|
||||
jour_period_obj.create(cr, uid, {
|
||||
'name': (journal.code or journal.name)+':'+(period.name or ''),
|
||||
|
||||
@@ -127,14 +127,14 @@ class account_fiscal_position(osv.osv):
|
||||
('auto_apply', '=', True),
|
||||
'|', ('vat_required', '=', False), ('vat_required', '=', partner.vat_subjected),
|
||||
]
|
||||
if delivery.country_id.id:
|
||||
fiscal_position_ids = self.search(cr, uid, domain + [('country_id', '=', delivery.country_id.id)], context=context, limit=1)
|
||||
if fiscal_position_ids:
|
||||
return fiscal_position_ids[0]
|
||||
|
||||
fiscal_position_ids = self.search(cr, uid, domain + [('country_id', '=', delivery.country_id.id)], context=context, limit=1)
|
||||
if fiscal_position_ids:
|
||||
return fiscal_position_ids[0]
|
||||
|
||||
fiscal_position_ids = self.search(cr, uid, domain + [('country_group_id.country_ids', '=', delivery.country_id.id)], context=context, limit=1)
|
||||
if fiscal_position_ids:
|
||||
return fiscal_position_ids[0]
|
||||
fiscal_position_ids = self.search(cr, uid, domain + [('country_group_id.country_ids', '=', delivery.country_id.id)], context=context, limit=1)
|
||||
if fiscal_position_ids:
|
||||
return fiscal_position_ids[0]
|
||||
|
||||
fiscal_position_ids = self.search(cr, uid, domain + [('country_id', '=', None), ('country_group_id', '=', None)], context=context, limit=1)
|
||||
if fiscal_position_ids:
|
||||
|
||||
@@ -36,8 +36,15 @@ class account_invoice_report(osv.osv):
|
||||
context={}
|
||||
currency_obj = self.pool.get('res.currency')
|
||||
currency_rate_obj = self.pool.get('res.currency.rate')
|
||||
user_currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
|
||||
currency_rate_id = currency_rate_obj.search(cr, uid, [('rate', '=', 1)], limit=1, context=context)[0]
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
user_currency_id = user.company_id.currency_id.id
|
||||
currency_rate_id = currency_rate_obj.search(
|
||||
cr, uid, [
|
||||
('rate', '=', 1),
|
||||
'|',
|
||||
('currency_id.company_id', '=', user.company_id.id),
|
||||
('currency_id.company_id', '=', False)
|
||||
], limit=1, context=context)[0]
|
||||
base_currency_id = currency_rate_obj.browse(cr, uid, currency_rate_id, context=context).currency_id.id
|
||||
res = {}
|
||||
ctx = context.copy()
|
||||
@@ -133,15 +140,7 @@ class account_invoice_report(osv.osv):
|
||||
SELECT min(ail.id) AS id,
|
||||
ai.date_invoice AS date,
|
||||
ail.product_id, ai.partner_id, ai.payment_term, ai.period_id, ail.account_analytic_id,
|
||||
CASE
|
||||
WHEN u.uom_type::text <> 'reference'::text
|
||||
THEN ( SELECT product_uom.name
|
||||
FROM product_uom
|
||||
WHERE product_uom.uom_type::text = 'reference'::text
|
||||
AND product_uom.active
|
||||
AND product_uom.category_id = u.category_id LIMIT 1)
|
||||
ELSE u.name
|
||||
END AS uom_name,
|
||||
u2.name AS uom_name,
|
||||
ai.currency_id, ai.journal_id, ai.fiscal_position, ai.user_id, ai.company_id,
|
||||
count(ail.*) AS nbr,
|
||||
ai.type, ai.state, pt.categ_id, ai.date_due, ai.account_id, ail.account_id AS account_line_id,
|
||||
@@ -161,11 +160,11 @@ class account_invoice_report(osv.osv):
|
||||
THEN SUM(- ail.price_subtotal)
|
||||
ELSE SUM(ail.price_subtotal)
|
||||
END / CASE
|
||||
WHEN SUM(ail.quantity / u.factor) <> 0::numeric
|
||||
WHEN SUM(ail.quantity / u.factor * u2.factor) <> 0::numeric
|
||||
THEN CASE
|
||||
WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying::text, 'in_invoice'::character varying::text])
|
||||
THEN SUM((- ail.quantity) / u.factor)
|
||||
ELSE SUM(ail.quantity / u.factor)
|
||||
THEN SUM((- ail.quantity) / u.factor * u2.factor)
|
||||
ELSE SUM(ail.quantity / u.factor * u2.factor)
|
||||
END
|
||||
ELSE 1::numeric
|
||||
END AS price_average,
|
||||
@@ -173,17 +172,8 @@ class account_invoice_report(osv.osv):
|
||||
WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying::text, 'in_invoice'::character varying::text])
|
||||
THEN - ai.residual
|
||||
ELSE ai.residual
|
||||
END / CASE
|
||||
WHEN (( SELECT count(l.id) AS count
|
||||
FROM account_invoice_line l
|
||||
LEFT JOIN account_invoice a ON a.id = l.invoice_id
|
||||
WHERE a.id = ai.id)) <> 0
|
||||
THEN ( SELECT count(l.id) AS count
|
||||
FROM account_invoice_line l
|
||||
LEFT JOIN account_invoice a ON a.id = l.invoice_id
|
||||
WHERE a.id = ai.id)
|
||||
ELSE 1::bigint
|
||||
END::numeric AS residual,
|
||||
END / (SELECT count(*) FROM account_invoice_line l where invoice_id = ai.id) *
|
||||
count(*) AS residual,
|
||||
ai.commercial_partner_id as commercial_partner_id,
|
||||
partner.country_id
|
||||
"""
|
||||
@@ -197,17 +187,17 @@ class account_invoice_report(osv.osv):
|
||||
LEFT JOIN product_product pr ON pr.id = ail.product_id
|
||||
left JOIN product_template pt ON pt.id = pr.product_tmpl_id
|
||||
LEFT JOIN product_uom u ON u.id = ail.uos_id
|
||||
LEFT JOIN product_uom u2 ON u.id = pt.uom_id
|
||||
LEFT JOIN product_uom u2 ON u2.id = pt.uom_id
|
||||
"""
|
||||
return from_str
|
||||
|
||||
def _group_by(self):
|
||||
group_by_str = """
|
||||
GROUP BY ail.product_id, ail.account_analytic_id, ai.date_invoice, ai.id,
|
||||
ai.partner_id, ai.payment_term, ai.period_id, u.name, ai.currency_id, ai.journal_id,
|
||||
ai.partner_id, ai.payment_term, ai.period_id, u2.name, u2.id, ai.currency_id, ai.journal_id,
|
||||
ai.fiscal_position, ai.user_id, ai.company_id, ai.type, ai.state, pt.categ_id,
|
||||
ai.date_due, ai.account_id, ail.account_id, ai.partner_bank_id, ai.residual,
|
||||
ai.amount_total, u.uom_type, u.category_id, ai.commercial_partner_id, partner.country_id
|
||||
ai.amount_total, ai.commercial_partner_id, partner.country_id
|
||||
"""
|
||||
return group_by_str
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from . import test_tax
|
||||
from . import test_search
|
||||
from . import test_reconciliation
|
||||
from . import test_account_move_closed_period
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
from datetime import date
|
||||
|
||||
from openerp.tests.common import TransactionCase
|
||||
from openerp.osv.orm import except_orm
|
||||
|
||||
class TestPeriodState(TransactionCase):
|
||||
"""
|
||||
Forbid creation of Journal Entries for a closed period.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestPeriodState, self).setUp()
|
||||
cr, uid = self.cr, self.uid
|
||||
self.wizard_period_close = self.registry('account.period.close')
|
||||
self.wizard_period_close_id = self.wizard_period_close.create(cr, uid, {'sure': 1})
|
||||
_, self.sale_journal_id = self.registry("ir.model.data").get_object_reference(cr, uid, "account", "sales_journal")
|
||||
_, self.period_9_id = self.registry("ir.model.data").get_object_reference(cr, uid, "account", "period_9")
|
||||
|
||||
def test_period_state(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
self.wizard_period_close.data_save(cr, uid, [self.wizard_period_close_id], {
|
||||
'lang': 'en_US',
|
||||
'active_model': 'account.period',
|
||||
'active_ids': [self.period_9_id],
|
||||
'tz': False,
|
||||
'active_id': self.period_9_id
|
||||
})
|
||||
with self.assertRaises(except_orm):
|
||||
self.registry('account.move').create(cr, uid, {
|
||||
'name': '/',
|
||||
'period_id': self.period_9_id,
|
||||
'journal_id': self.sale_journal_id,
|
||||
'date': date.today(),
|
||||
'line_id': [(0, 0, {
|
||||
'name': 'foo',
|
||||
'debit': 10,
|
||||
}), (0, 0, {
|
||||
'name': 'bar',
|
||||
'credit': 10,
|
||||
})]
|
||||
})
|
||||
@@ -75,6 +75,8 @@ class account_invoice_line(osv.osv):
|
||||
res: The move line entries produced so far by the parent move_line_get.
|
||||
"""
|
||||
inv = i_line.invoice_id
|
||||
fiscal_pool = self.pool.get('account.fiscal.position')
|
||||
fpos = inv.fiscal_position or False
|
||||
company_currency = inv.company_id.currency_id.id
|
||||
|
||||
if i_line.product_id.type != 'service' and i_line.product_id.valuation == 'real_time':
|
||||
@@ -110,7 +112,7 @@ class account_invoice_line(osv.osv):
|
||||
'price_unit':price_unit,
|
||||
'quantity':i_line.quantity,
|
||||
'price': -1 * self._get_price(cr, uid, inv, company_currency, i_line, price_unit),
|
||||
'account_id':cacc,
|
||||
'account_id':fiscal_pool.map_account(cr, uid, fpos, cacc),
|
||||
'product_id':i_line.product_id.id,
|
||||
'uos_id':i_line.uos_id.id,
|
||||
'account_analytic_id': False,
|
||||
|
||||
@@ -145,7 +145,6 @@
|
||||
<form string="Depreciation Lines">
|
||||
<group>
|
||||
<group>
|
||||
<field name="asset_id" invisible="1"/>
|
||||
<field name="parent_state" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="amount"/>
|
||||
@@ -156,8 +155,8 @@
|
||||
<group>
|
||||
<field name="sequence"/>
|
||||
<field name="depreciation_date"/>
|
||||
<field name="depreciated_value" readonly="1"/>
|
||||
<field name="remaining_value" readonly="1"/>
|
||||
<field name="depreciated_value"/>
|
||||
<field name="remaining_value"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
|
||||
@@ -100,9 +100,12 @@ class report_rappel(report_sxw.rml_parse):
|
||||
partner_max_text = i.followup_line_id.description
|
||||
text = partner_max_delay and partner_max_text or default_text
|
||||
if text:
|
||||
lang_obj = self.pool['res.lang']
|
||||
lang_ids = lang_obj.search(self.cr, self.uid, [('code', '=', stat_line.partner_id.lang)], context=context)
|
||||
date_format = lang_ids and lang_obj.browse(self.cr, self.uid, lang_ids[0], context=context).date_format or '%Y-%m-%d'
|
||||
text = text % {
|
||||
'partner_name': stat_line.partner_id.name,
|
||||
'date': time.strftime('%Y-%m-%d'),
|
||||
'date': time.strftime(date_format),
|
||||
'company_name': stat_line.company_id.name,
|
||||
'user_signature': self.pool['res.users'].browse(self.cr, self.uid, self.uid, context).signature or '',
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<template id="report_followup">
|
||||
<t t-call="report.html_container">
|
||||
<t t-foreach="ids_to_objects(data['form']['partner_ids'])" t-as="o">
|
||||
<t t-esc="setLang(o.partner_id.lang)"/>
|
||||
<t t-call="report.external_layout">
|
||||
<div class="page">
|
||||
<div class="row">
|
||||
|
||||
@@ -173,10 +173,11 @@ class calendar_attendee(osv.Model):
|
||||
return res
|
||||
|
||||
def _send_mail_to_attendees(self, cr, uid, ids, email_from=tools.config.get('email_from', False),
|
||||
template_xmlid='calendar_template_meeting_invitation', context=None):
|
||||
template_xmlid='calendar_template_meeting_invitation', force=False, context=None):
|
||||
"""
|
||||
Send mail for event invitation to event attendees.
|
||||
@param email_from: email address for user sending the mail
|
||||
@param force: If set to True, email will be sent to user himself. Usefull for example for alert, ...
|
||||
"""
|
||||
res = False
|
||||
|
||||
@@ -209,7 +210,7 @@ class calendar_attendee(osv.Model):
|
||||
})
|
||||
|
||||
for attendee in self.browse(cr, uid, ids, context=context):
|
||||
if attendee.email and email_from and attendee.email != email_from:
|
||||
if attendee.email and email_from and (attendee.email != email_from or force):
|
||||
ics_file = self.get_ics_file(cr, uid, attendee.event_id, context=context)
|
||||
mail_id = template_pool.send_mail(cr, uid, template_id, attendee.id, context=local_context)
|
||||
|
||||
@@ -511,7 +512,14 @@ class calendar_alarm_manager(osv.AbstractModel):
|
||||
alarm = self.pool['calendar.alarm'].browse(cr, uid, alert['alarm_id'], context=context)
|
||||
|
||||
if alarm.type == 'email':
|
||||
res = self.pool['calendar.attendee']._send_mail_to_attendees(cr, uid, [att.id for att in event.attendee_ids], template_xmlid='calendar_template_meeting_reminder', context=context)
|
||||
res = self.pool['calendar.attendee']._send_mail_to_attendees(
|
||||
cr,
|
||||
uid,
|
||||
[att.id for att in event.attendee_ids],
|
||||
template_xmlid='calendar_template_meeting_reminder',
|
||||
force=True,
|
||||
context=context
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
<field name="state_id" on_change="onchange_state(state_id)" options='{"no_open": True}' placeholder="State" style="width: 24%%"/>
|
||||
<field name="zip" placeholder="ZIP" style="width: 34%%"/>
|
||||
</div>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": True}'/>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": True, "no_create": True}'/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
@@ -492,7 +492,7 @@
|
||||
<field name="state_id" options='{"no_open": True}' on_change="onchange_state(state_id)" placeholder="State" style="width: 24%%"/>
|
||||
<field name="zip" placeholder="ZIP" style="width: 34%%"/>
|
||||
</div>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": True}'/>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": True, "no_create": True}'/>
|
||||
</div>
|
||||
</group>
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ from openerp.tools.translate import _
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
MAX_POP_MESSAGES = 50
|
||||
|
||||
class fetchmail_server(osv.osv):
|
||||
"""Incoming POP/IMAP mail server account"""
|
||||
@@ -223,27 +224,31 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
|
||||
imap_server.logout()
|
||||
elif server.type == 'pop':
|
||||
try:
|
||||
pop_server = server.connect()
|
||||
(numMsgs, totalSize) = pop_server.stat()
|
||||
pop_server.list()
|
||||
for num in range(1, numMsgs + 1):
|
||||
(header, msges, octets) = pop_server.retr(num)
|
||||
msg = '\n'.join(msges)
|
||||
res_id = None
|
||||
try:
|
||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||
msg,
|
||||
save_original=server.original,
|
||||
strip_attachments=(not server.attach),
|
||||
context=context)
|
||||
pop_server.dele(num)
|
||||
except Exception:
|
||||
_logger.info('Failed to process mail from %s server %s.', server.type, server.name, exc_info=True)
|
||||
failed += 1
|
||||
if res_id and server.action_id:
|
||||
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids': [res_id], 'active_model': context.get("thread_model", server.object_id.model)})
|
||||
cr.commit()
|
||||
_logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", numMsgs, server.type, server.name, (numMsgs - failed), failed)
|
||||
while True:
|
||||
pop_server = server.connect()
|
||||
(numMsgs, totalSize) = pop_server.stat()
|
||||
pop_server.list()
|
||||
for num in range(1, min(MAX_POP_MESSAGES, numMsgs) + 1):
|
||||
(header, msges, octets) = pop_server.retr(num)
|
||||
msg = '\n'.join(msges)
|
||||
res_id = None
|
||||
try:
|
||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||
msg,
|
||||
save_original=server.original,
|
||||
strip_attachments=(not server.attach),
|
||||
context=context)
|
||||
pop_server.dele(num)
|
||||
except Exception:
|
||||
_logger.info('Failed to process mail from %s server %s.', server.type, server.name, exc_info=True)
|
||||
failed += 1
|
||||
if res_id and server.action_id:
|
||||
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids': [res_id], 'active_model': context.get("thread_model", server.object_id.model)})
|
||||
cr.commit()
|
||||
if numMsgs < MAX_POP_MESSAGES:
|
||||
break
|
||||
pop_server.quit()
|
||||
_logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", numMsgs, server.type, server.name, (numMsgs - failed), failed)
|
||||
except Exception:
|
||||
_logger.info("General failure when trying to fetch mail from %s server %s.", server.type, server.name, exc_info=True)
|
||||
finally:
|
||||
|
||||
@@ -137,6 +137,7 @@ class google_service(osv.osv_memory):
|
||||
|
||||
status = 418
|
||||
response = ""
|
||||
ask_time = datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
try:
|
||||
if type.upper() == 'GET' or type.upper() == 'DELETE':
|
||||
data = werkzeug.url_encode(params)
|
||||
@@ -159,13 +160,16 @@ class google_service(osv.osv_memory):
|
||||
try:
|
||||
ask_time = datetime.strptime(request.headers.get('date'), "%a, %d %b %Y %H:%M:%S %Z")
|
||||
except:
|
||||
ask_time = datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
pass
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code in (400, 401, 410):
|
||||
raise e
|
||||
|
||||
_logger.exception("Bad google request : %s !" % e.read())
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong with your request to google"), context=context)
|
||||
elif e.code in (204, 404):
|
||||
status = e.code
|
||||
response = ""
|
||||
else:
|
||||
_logger.exception("Bad google request : %s !" % e.read())
|
||||
raise self.pool.get('res.config.settings').get_config_warning(cr, _("Something went wrong with your request to google"), context=context)
|
||||
return (status, response, ask_time)
|
||||
|
||||
def get_base_url(self, cr, uid, context=None):
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<page string="Personal Information" groups="base.group_hr_user">
|
||||
<group>
|
||||
<group string="Citizenship & Other Information">
|
||||
<field name="country_id" options='{"no_open": True}'/>
|
||||
<field name="country_id" options='{"no_open": True, "no_create": True}'/>
|
||||
<field name="identification_id" groups="base.group_hr_user"/>
|
||||
<field name="passport_id" groups="base.group_hr_user"/>
|
||||
<field name="bank_account_id"/>
|
||||
|
||||
@@ -150,10 +150,7 @@ class hr_employee(osv.osv):
|
||||
|
||||
def _attendance_access(self, cr, uid, ids, name, args, context=None):
|
||||
# this function field use to hide attendance button to singin/singout from menu
|
||||
group = self.pool.get('ir.model.data').get_object(cr, uid, 'base', 'group_hr_attendance')
|
||||
visible = False
|
||||
if uid in [user.id for user in group.users]:
|
||||
visible = True
|
||||
visible = self.pool.get("res.users").has_group(cr, uid, "base.group_hr_attendance")
|
||||
return dict([(x, visible) for x in ids])
|
||||
|
||||
_columns = {
|
||||
|
||||
@@ -327,7 +327,8 @@ class hr_timesheet_line(osv.osv):
|
||||
for ts_line in self.browse(cursor, user, ids, context=context):
|
||||
sheet_ids = sheet_obj.search(cursor, user,
|
||||
[('date_to', '>=', ts_line.date), ('date_from', '<=', ts_line.date),
|
||||
('employee_id.user_id', '=', ts_line.user_id.id)],
|
||||
('employee_id.user_id', '=', ts_line.user_id.id),
|
||||
('state', 'in', ['draft', 'new'])],
|
||||
context=context)
|
||||
if sheet_ids:
|
||||
# [0] because only one sheet possible for an employee between 2 dates
|
||||
@@ -366,17 +367,11 @@ class hr_timesheet_line(osv.osv):
|
||||
),
|
||||
}
|
||||
|
||||
def _check_sheet_state(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
for timesheet_line in self.browse(cr, uid, ids, context=context):
|
||||
if timesheet_line.sheet_id and timesheet_line.sheet_id.state not in ('draft', 'new'):
|
||||
return False
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_sheet_state, 'You cannot modify an entry in a Confirmed/Done timesheet !', ['state']),
|
||||
]
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
self._check(cr, uid, ids)
|
||||
return super(hr_timesheet_line, self).write(cr, uid, ids, values, context=context)
|
||||
|
||||
def unlink(self, cr, uid, ids, *args, **kwargs):
|
||||
if isinstance(ids, (int, long)):
|
||||
|
||||
@@ -260,7 +260,9 @@ openerp.hr_timesheet_sheet = function(instance) {
|
||||
return;
|
||||
}
|
||||
var ops = self.generate_o2m_value();
|
||||
new instance.web.Model("hr.analytic.timesheet").call("on_change_account_id", [[], id]).then(function(res) {
|
||||
new instance.web.Model("hr.analytic.timesheet").call("multi_on_change_account_id", [[], [id],
|
||||
new instance.web.CompoundContext({'user_id': self.get('user_id')})]).then(function(res) {
|
||||
res = res[id];
|
||||
var def = _.extend({}, self.default_get, res.value, {
|
||||
name: self.description_line,
|
||||
unit_amount: 0,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
The escpos directory contains the MIT licensed pyxmlescpos lib taken from
|
||||
|
||||
https://github.com/fvdsn/py-xml-escpos
|
||||
|
||||
@@ -16,6 +16,14 @@ import pickle
|
||||
import re
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
try:
|
||||
from .. escpos import *
|
||||
from .. escpos.exceptions import *
|
||||
from .. escpos.printer import Usb
|
||||
except ImportError:
|
||||
escpos = printer = None
|
||||
|
||||
from threading import Thread, Lock
|
||||
from Queue import Queue, Empty
|
||||
|
||||
@@ -24,13 +32,6 @@ try:
|
||||
except ImportError:
|
||||
usb = None
|
||||
|
||||
try:
|
||||
from .. import escpos
|
||||
from ..escpos import printer
|
||||
from ..escpos import supported_devices
|
||||
except ImportError:
|
||||
escpos = printer = None
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from openerp import http
|
||||
@@ -110,24 +111,19 @@ class EscposDriver(Thread):
|
||||
self.start()
|
||||
|
||||
def get_escpos_printer(self):
|
||||
try:
|
||||
printers = self.connected_usb_devices()
|
||||
if len(printers) > 0:
|
||||
self.set_status('connected','Connected to '+printers[0]['name'])
|
||||
return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product'])
|
||||
else:
|
||||
self.set_status('disconnected','Printer Not Found')
|
||||
return None
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
|
||||
printers = self.connected_usb_devices()
|
||||
if len(printers) > 0:
|
||||
self.set_status('connected','Connected to '+printers[0]['name'])
|
||||
return Usb(printers[0]['vendor'], printers[0]['product'])
|
||||
else:
|
||||
self.set_status('disconnected','Printer Not Found')
|
||||
return None
|
||||
|
||||
def get_status(self):
|
||||
self.push_task('status')
|
||||
return self.status
|
||||
|
||||
|
||||
|
||||
def open_cashbox(self,printer):
|
||||
printer.cashdraw(2)
|
||||
printer.cashdraw(5)
|
||||
@@ -150,11 +146,13 @@ class EscposDriver(Thread):
|
||||
_logger.warning('ESC/POS Device Disconnected: '+message)
|
||||
|
||||
def run(self):
|
||||
|
||||
if not escpos:
|
||||
_logger.error('ESC/POS cannot initialize, please verify system dependencies.')
|
||||
return
|
||||
while True:
|
||||
try:
|
||||
error = True
|
||||
timestamp, task, data = self.queue.get(True)
|
||||
|
||||
printer = self.get_escpos_printer()
|
||||
@@ -162,6 +160,7 @@ class EscposDriver(Thread):
|
||||
if printer == None:
|
||||
if task != 'status':
|
||||
self.queue.put((timestamp,task,data))
|
||||
error = False
|
||||
time.sleep(5)
|
||||
continue
|
||||
elif task == 'receipt':
|
||||
@@ -178,11 +177,25 @@ class EscposDriver(Thread):
|
||||
self.print_status(printer)
|
||||
elif task == 'status':
|
||||
pass
|
||||
error = False
|
||||
|
||||
except NoDeviceError as e:
|
||||
print "No device found %s" %str(e)
|
||||
except HandleDeviceError as e:
|
||||
print "Impossible to handle the device due to previous error %s" % str(e)
|
||||
except TicketNotPrinted as e:
|
||||
print "The ticket does not seems to have been fully printed %s" % str(e)
|
||||
except NoStatusError as e:
|
||||
print "Impossible to get the status of the printer %s" % str(e)
|
||||
except Exception as e:
|
||||
self.set_status('error', str(e))
|
||||
errmsg = str(e) + '\n' + '-'*60+'\n' + traceback.format_exc() + '-'*60 + '\n'
|
||||
_logger.error(errmsg);
|
||||
finally:
|
||||
if error:
|
||||
self.queue.put((timestamp, task, data))
|
||||
if printer:
|
||||
printer.close()
|
||||
|
||||
def push_task(self,task, data = None):
|
||||
self.lockedstart()
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Frederic van der Essen & Manuel F. Martinez
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -8,6 +8,13 @@ CTL_FF = '\x0c' # Form feed
|
||||
CTL_CR = '\x0d' # Carriage return
|
||||
CTL_HT = '\x09' # Horizontal tab
|
||||
CTL_VT = '\x0b' # Vertical tab
|
||||
|
||||
# RT Status commands
|
||||
DLE_EOT_PRINTER = '\x10\x04\x01' # Transmit printer status
|
||||
DLE_EOT_OFFLINE = '\x10\x04\x02'
|
||||
DLE_EOT_ERROR = '\x10\x04\x03'
|
||||
DLE_EOT_PAPER = '\x10\x04\x04'
|
||||
|
||||
# Printer hardware
|
||||
HW_INIT = '\x1b\x40' # Clear data in buffer and reset modes
|
||||
HW_SELECT = '\x1b\x3d\x01' # Printer select
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@author: Manuel F Martinez <manpaz@bashlinux.com>
|
||||
@organization: Bashlinux
|
||||
@copyright: Copyright (c) 2012 Bashlinux
|
||||
@license: GPL
|
||||
'''
|
||||
|
||||
|
||||
import logging
|
||||
import time
|
||||
import copy
|
||||
import io
|
||||
@@ -21,19 +13,15 @@ import xml.dom.minidom as minidom
|
||||
|
||||
from PIL import Image
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import jcconv
|
||||
except ImportError:
|
||||
jcconv = None
|
||||
_logger.warning('ESC/POS: please install jcconv for improved Japanese receipt printing:\n # pip install jcconv')
|
||||
|
||||
try:
|
||||
import qrcode
|
||||
except ImportError:
|
||||
qrcode = None
|
||||
_logger.warning('ESC/POS: please install the qrcode python module for qrcode printing in point of sale receipts:\n # pip install qrcode')
|
||||
|
||||
from constants import *
|
||||
from exceptions import *
|
||||
@@ -107,14 +95,14 @@ class StyleStack:
|
||||
'off': TXT_BOLD_OFF,
|
||||
'on': TXT_BOLD_ON,
|
||||
# must be issued after 'size' command
|
||||
# because ESC ! resets ESC E
|
||||
# because ESC ! resets ESC -
|
||||
'_order': 10,
|
||||
},
|
||||
'font': {
|
||||
'a': TXT_FONT_A,
|
||||
'b': TXT_FONT_B,
|
||||
# must be issued after 'size' command
|
||||
# because ESC ! resets ESC M
|
||||
# because ESC ! resets ESC -
|
||||
'_order': 10,
|
||||
},
|
||||
'size': {
|
||||
@@ -122,7 +110,7 @@ class StyleStack:
|
||||
'double-height': TXT_2HEIGHT,
|
||||
'double-width': TXT_2WIDTH,
|
||||
'double': TXT_DOUBLE,
|
||||
'_order': 1,
|
||||
'_order': 1,
|
||||
},
|
||||
'color': {
|
||||
'black': TXT_COLOR_BLACK,
|
||||
@@ -181,9 +169,8 @@ class StyleStack:
|
||||
def to_escpos(self):
|
||||
""" converts the current style to an escpos command string """
|
||||
cmd = ''
|
||||
# Sort commands because some commands affect others (see _order attributes above)
|
||||
ordered_cmds = self.cmds.keys()
|
||||
ordered_cmds.sort(lambda x, y: cmp(self.cmds[x]['_order'], self.cmds[y]['_order']))
|
||||
ordered_cmds.sort(lambda x,y: cmp(self.cmds[x]['_order'], self.cmds[y]['_order']))
|
||||
for style in ordered_cmds:
|
||||
cmd += self.cmds[style][self.get(style)]
|
||||
return cmd
|
||||
|
||||
@@ -78,3 +78,39 @@ class CashDrawerError(Error):
|
||||
|
||||
def __str__(self):
|
||||
return "Valid pin must be set to send pulse"
|
||||
|
||||
class NoStatusError(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 70
|
||||
|
||||
def __str__(self):
|
||||
return "Impossible to get status from the printer"
|
||||
|
||||
class TicketNotPrinted(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 80
|
||||
|
||||
def __str__(self):
|
||||
return "A part of the ticket was not been printed"
|
||||
|
||||
class NoDeviceError(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 90
|
||||
|
||||
def __str__(self):
|
||||
return "Impossible to find the printer Device"
|
||||
|
||||
class HandleDeviceError(Error):
|
||||
def __init__(self, msg=""):
|
||||
Error.__init__(self, msg)
|
||||
self.msg = msg
|
||||
self.resultcode = 100
|
||||
|
||||
def __str__(self):
|
||||
return "Impossible to handle device"
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
'''
|
||||
@author: Manuel F Martinez <manpaz@bashlinux.com>
|
||||
@organization: Bashlinux
|
||||
@copyright: Copyright (c) 2012 Bashlinux
|
||||
@license: GPL
|
||||
'''
|
||||
|
||||
import usb.core
|
||||
import usb.util
|
||||
@@ -14,6 +8,7 @@ import socket
|
||||
from escpos import *
|
||||
from constants import *
|
||||
from exceptions import *
|
||||
from time import sleep
|
||||
|
||||
class Usb(Escpos):
|
||||
""" Define USB printer """
|
||||
@@ -26,6 +21,9 @@ class Usb(Escpos):
|
||||
@param in_ep : Input end point
|
||||
@param out_ep : Output end point
|
||||
"""
|
||||
|
||||
self.errorText = "ERROR PRINTER\n\n\n\n\n\n"+PAPER_FULL_CUT
|
||||
|
||||
self.idVendor = idVendor
|
||||
self.idProduct = idProduct
|
||||
self.interface = interface
|
||||
@@ -33,35 +31,102 @@ class Usb(Escpos):
|
||||
self.out_ep = out_ep
|
||||
self.open()
|
||||
|
||||
|
||||
def open(self):
|
||||
""" Search device on USB tree and set is as escpos device """
|
||||
|
||||
self.device = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
|
||||
if self.device is None:
|
||||
print "Cable isn't plugged in"
|
||||
|
||||
if self.device.is_kernel_driver_active(0):
|
||||
try:
|
||||
self.device.detach_kernel_driver(0)
|
||||
except usb.core.USBError as e:
|
||||
print "Could not detatch kernel driver: %s" % str(e)
|
||||
|
||||
raise NoDeviceError()
|
||||
try:
|
||||
if self.device.is_kernel_driver_active(self.interface):
|
||||
self.device.detach_kernel_driver(self.interface)
|
||||
self.device.set_configuration()
|
||||
self.device.reset()
|
||||
usb.util.claim_interface(self.device, self.interface)
|
||||
except usb.core.USBError as e:
|
||||
print "Could not set configuration: %s" % str(e)
|
||||
raise HandleDeviceError(e)
|
||||
|
||||
def close(self):
|
||||
i = 0
|
||||
while True:
|
||||
try:
|
||||
if not self.device.is_kernel_driver_active(self.interface):
|
||||
usb.util.release_interface(self.device, self.interface)
|
||||
self.device.attach_kernel_driver(self.interface)
|
||||
usb.util.dispose_resources(self.device)
|
||||
else:
|
||||
self.device = None
|
||||
return True
|
||||
except usb.core.USBError as e:
|
||||
i += 1
|
||||
if i > 100:
|
||||
return False
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
def _raw(self, msg):
|
||||
""" Print any command sent in raw format """
|
||||
self.device.write(self.out_ep, msg, self.interface)
|
||||
if len(msg) != self.device.write(self.out_ep, msg, self.interface):
|
||||
self.device.write(self.out_ep, self.errorText, self.interface)
|
||||
raise TicketNotPrinted()
|
||||
|
||||
def __extract_status(self):
|
||||
maxiterate = 0
|
||||
rep = None
|
||||
while rep == None:
|
||||
maxiterate += 1
|
||||
if maxiterate > 10000:
|
||||
raise NoStatusError()
|
||||
r = self.device.read(self.in_ep, 20, self.interface).tolist()
|
||||
while len(r):
|
||||
rep = r.pop()
|
||||
return rep
|
||||
|
||||
def get_printer_status(self):
|
||||
status = {
|
||||
'printer': {},
|
||||
'offline': {},
|
||||
'error' : {},
|
||||
'paper' : {},
|
||||
}
|
||||
|
||||
self.device.write(self.out_ep, DLE_EOT_PRINTER, self.interface)
|
||||
printer = self.__extract_status()
|
||||
self.device.write(self.out_ep, DLE_EOT_OFFLINE, self.interface)
|
||||
offline = self.__extract_status()
|
||||
self.device.write(self.out_ep, DLE_EOT_ERROR, self.interface)
|
||||
error = self.__extract_status()
|
||||
self.device.write(self.out_ep, DLE_EOT_PAPER, self.interface)
|
||||
paper = self.__extract_status()
|
||||
|
||||
status['printer']['status_code'] = printer
|
||||
status['printer']['status_error'] = not ((printer & 147) == 18)
|
||||
status['printer']['online'] = not bool(printer & 8)
|
||||
status['printer']['recovery'] = bool(printer & 32)
|
||||
status['printer']['paper_feed_on'] = bool(printer & 64)
|
||||
status['printer']['drawer_pin_high'] = bool(printer & 4)
|
||||
status['offline']['status_code'] = offline
|
||||
status['offline']['status_error'] = not ((offline & 147) == 18)
|
||||
status['offline']['cover_open'] = bool(offline & 4)
|
||||
status['offline']['paper_feed_on'] = bool(offline & 8)
|
||||
status['offline']['paper'] = not bool(offline & 32)
|
||||
status['offline']['error'] = bool(offline & 64)
|
||||
status['error']['status_code'] = error
|
||||
status['error']['status_error'] = not ((error & 147) == 18)
|
||||
status['error']['recoverable'] = bool(error & 4)
|
||||
status['error']['autocutter'] = bool(error & 8)
|
||||
status['error']['unrecoverable'] = bool(error & 32)
|
||||
status['error']['auto_recoverable'] = not bool(error & 64)
|
||||
status['paper']['status_code'] = paper
|
||||
status['paper']['status_error'] = not ((paper & 147) == 18)
|
||||
status['paper']['near_end'] = bool(paper & 12)
|
||||
status['paper']['present'] = not bool(paper & 96)
|
||||
|
||||
return status
|
||||
|
||||
def __del__(self):
|
||||
""" Release USB interface """
|
||||
if self.device:
|
||||
usb.util.dispose_resources(self.device)
|
||||
self.close()
|
||||
self.device = None
|
||||
|
||||
|
||||
@@ -134,3 +199,4 @@ class Network(Escpos):
|
||||
def __del__(self):
|
||||
""" Close TCP connection """
|
||||
self.device.close()
|
||||
|
||||
|
||||
@@ -168,41 +168,41 @@ class Scanner(Thread):
|
||||
device.ungrab()
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
device = self.get_device()
|
||||
if not device:
|
||||
time.sleep(5) # wait until a suitable device is plugged
|
||||
else:
|
||||
try:
|
||||
device.grab()
|
||||
shift = False
|
||||
barcode = []
|
||||
time.sleep(5) # wait until a suitable device is plugged
|
||||
device = self.get_device()
|
||||
|
||||
while True: # keycode loop
|
||||
r,w,x = select([device],[],[],5)
|
||||
if len(r) == 0: # timeout
|
||||
break
|
||||
events = device.read()
|
||||
try:
|
||||
device.grab()
|
||||
shift = False
|
||||
barcode = []
|
||||
|
||||
for event in events:
|
||||
if event.type == evdev.ecodes.EV_KEY:
|
||||
#_logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
|
||||
if event.value == 1: # keydown events
|
||||
if event.code in self.keymap:
|
||||
if shift:
|
||||
barcode.append(self.keymap[event.code][1])
|
||||
else:
|
||||
barcode.append(self.keymap[event.code][0])
|
||||
elif event.code == 42 or event.code == 54: # SHIFT
|
||||
shift = True
|
||||
elif event.code == 28: # ENTER, end of barcode
|
||||
self.barcodes.put( (time.time(),''.join(barcode)) )
|
||||
barcode = []
|
||||
elif event.value == 0: #keyup events
|
||||
if event.code == 42 or event.code == 54: # LEFT SHIFT
|
||||
shift = False
|
||||
while True: # keycode loop
|
||||
r,w,x = select([device],[],[],5)
|
||||
if len(r) == 0: # timeout
|
||||
break
|
||||
events = device.read()
|
||||
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
for event in events:
|
||||
if event.type == evdev.ecodes.EV_KEY:
|
||||
#_logger.debug('Evdev Keyboard event %s',evdev.categorize(event))
|
||||
if event.value == 1: # keydown events
|
||||
if event.code in self.keymap:
|
||||
if shift:
|
||||
barcode.append(self.keymap[event.code][1])
|
||||
else:
|
||||
barcode.append(self.keymap[event.code][0])
|
||||
elif event.code == 42 or event.code == 54: # SHIFT
|
||||
shift = True
|
||||
elif event.code == 28: # ENTER, end of barcode
|
||||
self.barcodes.put( (time.time(),''.join(barcode)) )
|
||||
barcode = []
|
||||
elif event.value == 0: #keyup events
|
||||
if event.code == 42 or event.code == 54: # LEFT SHIFT
|
||||
shift = False
|
||||
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
|
||||
s = Scanner()
|
||||
|
||||
@@ -212,5 +212,4 @@ class ScannerDriver(hw_proxy.Proxy):
|
||||
@http.route('/hw_proxy/scanner', type='json', auth='none', cors='*')
|
||||
def scanner(self):
|
||||
return s.get_barcode()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Business Applications
|
||||
# Copyright (C) 2014-2015 Odoo S.A. <http://www.odoo.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
import l10n_be_intrastat
|
||||
import wizard
|
||||
@@ -0,0 +1,43 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Business Applications
|
||||
# Copyright (C) 2014-2015 Odoo S.A. <http://www.odoo.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': 'Belgian Intrastat Declaration',
|
||||
'version': '1.0',
|
||||
'category': 'Reporting',
|
||||
'description': """
|
||||
Generates Intrastat XML report for declaration
|
||||
Based on invoices.
|
||||
""",
|
||||
'author': 'Odoo SA',
|
||||
'depends': ['report_intrastat', 'sale_stock', 'account_accountant', 'l10n_be'],
|
||||
'data': [
|
||||
'data/regions.xml',
|
||||
'data/report.intrastat.code.csv',
|
||||
'data/transaction.codes.xml',
|
||||
'data/transport.modes.xml',
|
||||
'security/groups.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'l10n_be_intrastat.xml',
|
||||
'wizard/l10n_be_intrastat_xml_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="l10n_be_intrastat.intrastat_region_1" model="l10n_be_intrastat.region">
|
||||
<field name="code">1</field>
|
||||
<field name="name">Flemish region</field>
|
||||
<field name="country_id" ref="base.be"/>
|
||||
<field name="description">Includes Antwerp, Flemish Brabant, West Flanders, East Flanders and Limburg provinces</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_region_2" model="l10n_be_intrastat.region">
|
||||
<field name="code">2</field>
|
||||
<field name="name">Walloon region</field>
|
||||
<field name="country_id" ref="base.be"/>
|
||||
<field name="description">Includes Walloon Brabant, Hainaut, Liège, Luxembourg and Namur provinces</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_region_3" model="l10n_be_intrastat.region">
|
||||
<field name="code">3</field>
|
||||
<field name="name">Brussels region</field>
|
||||
<field name="country_id" ref="base.be"/>
|
||||
<field name="description">Includes 19 districts</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_1" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">1</field>
|
||||
<field name="description">Transactions involving an actual or intended transfer of ownership from residents to non-residents against financial or other compensation (except the transactions listed under 2, 7 and 8)</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_2" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">2</field>
|
||||
<field name="description">Return and replacement of goods free of charge after registration of the original transaction</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_3" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">3</field>
|
||||
<field name="description">Transactions involving transfer of ownership without financial or in kind compensation (e.g. aid shipments)</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_4" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">4</field>
|
||||
<field name="description">Operations with a view to processing 1 under contract (no transfer of ownership to the processor)</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_5" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">5</field>
|
||||
<field name="description">Operations following processing under contract (no transfer of ownership to the processor)</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_6" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">6</field>
|
||||
<field name="description">Operations "following" repair or maintenance, other than under joint inter-governmental production programmes. Repair and maintenance can be against payment or free of charge. These operations do not involve a (future) transfer of ownership*.</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_7" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">7</field>
|
||||
<field name="description">Operations under joint defence projects or other joint intergovernmental production programmes</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_8" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">8</field>
|
||||
<field name="description">Transactions involving the supply of building materials and technical equipment under a general construction or civil engineering contract for which no separate invoicing of the goods is required and an invoice for the total contract is issued</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_transaction_9" model="l10n_be_intrastat.transaction">
|
||||
<field name="code">9</field>
|
||||
<field name="description">Other transactions which cannot be classified under other codes</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_1" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">1</field>
|
||||
<field name="name">Sea transport (including wagons, motor vehicles, trailers, semi-trailers and lighters on board of a ship)</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_2" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">2</field>
|
||||
<field name="name">Railway transport (including lorries on railway wagons)</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_3" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">3</field>
|
||||
<field name="name">Road transport</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_4" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">4</field>
|
||||
<field name="name">Air transport</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_5" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">5</field>
|
||||
<field name="name">Postal consignments</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_7" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">7</field>
|
||||
<field name="name">Fixed transport installations (e.g. pipelines, high-tension cables)</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_8" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">8</field>
|
||||
<field name="name">Inland waterway transport</field>
|
||||
</record>
|
||||
<record id="l10n_be_intrastat.intrastat_trmode_9" model="l10n_be_intrastat.transport_mode">
|
||||
<field name="code">9</field>
|
||||
<field name="name">Own propulsion (imported or exported means of transport crossing the border under their own power, e.g. aircraft, lorries, boats, etc.)</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,67 @@
|
||||
Intrastat Module
|
||||
++++++++++++++++
|
||||
|
||||
This module can generate legal electronic reports for intrastat declaration
|
||||
|
||||
1 Security groups
|
||||
*****************
|
||||
|
||||
There's two security groups, a standard and an extended, that can be used to view/hide fields linked to the intrastat declaration. The extended group is mainly here to hide the two useless fields if you don't have to send an extended declaration, "Means of transport" and "Incoterm".
|
||||
|
||||
2 Intrastat codes
|
||||
*****************
|
||||
|
||||
The system must be able to associate an intrastat code to each product exported. The system can do this in different ways.
|
||||
|
||||
Each product can have a category, and each category can have a parent category. To retrieve the intrastat code associated to a product, the system search in this order :
|
||||
|
||||
#. check if product have an intrastat code
|
||||
#. if not, check if product's category have an intrastat code
|
||||
#. if not, check parent category if it have an intrastat code
|
||||
#. if not, check parent category of parent category if it have an intrastat code (and so on)
|
||||
#. if none found, an error is shown
|
||||
|
||||
So, in a user point of view, to easily record your intrastat codes, you can procede on the low-level categories, and then, for the product in a category with a wrong code for this item, set the intrastat code in the product form
|
||||
|
||||
3 Country of destination
|
||||
************************
|
||||
|
||||
Intrastat declares all movements from an EU member country to another. In an invoice, a zone called "intrastat country" is used to determine the destination of goods. If this field is empty, the invoiced partner's country is used.
|
||||
|
||||
==============
|
||||
Direct invoice
|
||||
==============
|
||||
|
||||
If you create an invoice directly, the intrastat country is empty by default, and the invoiced partner's country is used. If the country of destination is different from the country of invoice, simply fill in the field "Intrastat country".
|
||||
|
||||
=======================
|
||||
Invoice from sale order
|
||||
=======================
|
||||
|
||||
If you create an invoice from a sale order, there are two cases. If you're not using different addresses for invoicing and delivery, the country of the partner is used to fill in the "Intrastat country" of the invoice
|
||||
If you're using different addresses, the country of delivery address is used, if there's none, the country of the partner's address, and if none, the country of invoice address.
|
||||
|
||||
=====================
|
||||
Invoice from purchase
|
||||
=====================
|
||||
|
||||
If you create an invoice from a purchase order, the "Intrastat country" field is filled with the partner address.
|
||||
|
||||
4 Regions, Type of transaction
|
||||
******************************
|
||||
|
||||
In the intrastat declaration the system must fill an information regarding the region of origin of products, and an information regarding the type of transaction.
|
||||
|
||||
======
|
||||
Region
|
||||
======
|
||||
|
||||
The region of origin of products is determined by using two parameters. If your invoice is linked to a sale order or to a purchase order, the system go to the order, check if the order is linked to a warehouse, and check the warehouse region.
|
||||
|
||||
If the system hasn't find a region, the system goes to the company, and check the region of the company. If there's none, an error message is generated.
|
||||
|
||||
===================
|
||||
Type of transaction
|
||||
===================
|
||||
|
||||
Intrastat define a list of transactions types. To determine the type of transaction for a particular invoice, the system check if a transaction type is selected in the invoice. If not, the type 1 is selected by default.
|
||||
@@ -0,0 +1,454 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * l10n_be_intrastat
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 8.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-02-03 18:32+0000\n"
|
||||
"PO-Revision-Date: 2015-02-03 18:32+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,arrivals:0
|
||||
msgid "Arrivals"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:l10n_be_intrastat.region,name:l10n_be_intrastat.intrastat_region_3
|
||||
msgid "Brussels region"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat_xml.xml_decl:l10n_be_intrastat.view_intrastat_declaration_xml
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat_xml.xml_decl:l10n_be_intrastat.view_intrastat_declaration_xml
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,code:0
|
||||
#: field:l10n_be_intrastat.transaction,code:0
|
||||
#: field:l10n_be_intrastat.transport_mode,code:0
|
||||
msgid "Code"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: sql_constraint:l10n_be_intrastat.region:0
|
||||
#: sql_constraint:l10n_be_intrastat.transaction:0
|
||||
#: sql_constraint:l10n_be_intrastat.transport_mode:0
|
||||
msgid "Code must be unique."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,tax_code_id:0
|
||||
msgid "Company Tax Chart"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,country_id:0
|
||||
msgid "Country"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat_xml.xml_decl:l10n_be_intrastat.view_intrastat_declaration_xml
|
||||
msgid "Create XML"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,create_uid:0
|
||||
#: field:l10n_be_intrastat.transaction,create_uid:0
|
||||
#: field:l10n_be_intrastat.transport_mode,create_uid:0
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,create_uid:0
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,create_date:0
|
||||
#: field:l10n_be_intrastat.transaction,create_date:0
|
||||
#: field:l10n_be_intrastat.transport_mode,create_date:0
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,create_date:0
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:res.company,incoterm_id:0
|
||||
msgid "Default incoterm for Intrastat"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:res.company,transport_mode_id:0
|
||||
msgid "Default transport mode"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,description:0
|
||||
#: field:l10n_be_intrastat.transaction,description:0
|
||||
#: field:l10n_be_intrastat.transport_mode,name:0
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,dispatches:0
|
||||
msgid "Dispatches"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,state:0
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,state:0
|
||||
msgid "Draft"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,arrivals:0
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,dispatches:0
|
||||
msgid "Exempt"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,arrivals:0
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,dispatches:0
|
||||
msgid "Extended"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,name:0
|
||||
msgid "File Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:l10n_be_intrastat.region,name:l10n_be_intrastat.intrastat_region_1
|
||||
msgid "Flemish region"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:96
|
||||
#, python-format
|
||||
msgid "Go to company configuration screen"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat_xml.xml_decl:l10n_be_intrastat.view_intrastat_declaration_xml
|
||||
msgid "Here is the XML file with your intrastat declaration:"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,id:0
|
||||
#: field:l10n_be_intrastat.transaction,id:0
|
||||
#: field:l10n_be_intrastat.transport_mode,id:0
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,id:0
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:account.invoice,incoterm_id:0
|
||||
msgid "Incoterm"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: help:account.invoice,incoterm_id:0
|
||||
#: help:res.company,incoterm_id:0
|
||||
msgid "International Commercial Terms are a series of predefined commercial terms used in international transactions."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:product.category:l10n_be_intrastat.l10n_be_intrastat_xml_decl_product_category_view
|
||||
msgid "Intrastat"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:product.category,intrastat_id:0
|
||||
msgid "Intrastat Code"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:account.invoice,intrastat_country_id:0
|
||||
msgid "Intrastat Country"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.actions.act_window,name:l10n_be_intrastat.action_intrastat_xml_declaration
|
||||
#: model:ir.ui.menu,name:l10n_be_intrastat.l10n_be_intrastat_xml_declaration
|
||||
#: view:l10n_be_intrastat_xml.xml_decl:l10n_be_intrastat.view_intrastat_declaration_xml
|
||||
msgid "Intrastat Declaration"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,file_save:0
|
||||
msgid "Intrastat Report File"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:account.invoice,intrastat_transaction_id:0
|
||||
msgid "Intrastat Transaction Type"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:account.invoice,transport_mode_id:0
|
||||
msgid "Intrastat Transport Mode"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_l10n_be_intrastat_xml_xml_decl
|
||||
msgid "Intrastat XML Declaration"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_report_intrastat_code
|
||||
msgid "Intrastat code"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: help:account.invoice,intrastat_country_id:0
|
||||
msgid "Intrastat country, delivery for sales, origin for purchases"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:res.groups,name:l10n_be_intrastat.intrastat_extended
|
||||
msgid "Intrastat extended"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: help:account.invoice,intrastat_transaction_id:0
|
||||
msgid "Intrastat nature of transaction"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:res.company,region_id:0
|
||||
#: field:stock.warehouse,region_id:0
|
||||
msgid "Intrastat region"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat.transaction:l10n_be_intrastat.intra_view_transaction_form
|
||||
msgid "Intrastat transaction form"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat.transaction:l10n_be_intrastat.intra_view_transaction_tree
|
||||
msgid "Intrastat transaction types"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat.transport_mode:l10n_be_intrastat.intra_view_transport_mode_form
|
||||
msgid "Intrastat transportation mode form"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat.transport_mode:l10n_be_intrastat.intra_view_transport_mode_tree
|
||||
msgid "Intrastat transportation modes"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_account_invoice
|
||||
msgid "Invoice"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,write_uid:0
|
||||
#: field:l10n_be_intrastat.transaction,write_uid:0
|
||||
#: field:l10n_be_intrastat.transport_mode,write_uid:0
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,write_uid:0
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,write_date:0
|
||||
#: field:l10n_be_intrastat.transaction,write_date:0
|
||||
#: field:l10n_be_intrastat.transport_mode,write_date:0
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,write_date:0
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "Month"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat.region,name:0
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_product_product
|
||||
msgid "Product"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:291
|
||||
#, python-format
|
||||
msgid "Product \"%s\" has no intrastat code, please configure it"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_product_category
|
||||
msgid "Product Category"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_purchase_order
|
||||
msgid "Purchase Order"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_sale_order
|
||||
msgid "Sales Order"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:156
|
||||
#, python-format
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,month:0
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,arrivals:0
|
||||
#: selection:l10n_be_intrastat_xml.xml_decl,dispatches:0
|
||||
msgid "Standard"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,state:0
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:280
|
||||
#, python-format
|
||||
msgid "The Intrastat Region of the selected company is not set, please make sure to configure it first."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:109
|
||||
#, python-format
|
||||
msgid "The country of your company is not set, please make sure to configure it first."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:316
|
||||
#, python-format
|
||||
msgid "The default Incoterm of your company is not set, please make sure to configure it first."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:304
|
||||
#, python-format
|
||||
msgid "The default Intrastat transport mode of your company is not set, please make sure to configure it first."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:116
|
||||
#, python-format
|
||||
msgid "The registry number of your company is not set, please make sure to configure it first."
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:l10n_be_intrastat.region,name:l10n_be_intrastat.intrastat_region_2
|
||||
msgid "Walloon region"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: model:ir.model,name:l10n_be_intrastat.model_stock_warehouse
|
||||
msgid "Warehouse"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat_xml.xml_decl:l10n_be_intrastat.view_intrastat_declaration_xml
|
||||
msgid "XML Intratstat Declaration"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: field:l10n_be_intrastat_xml.xml_decl,year:0
|
||||
msgid "Year"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: code:addons/l10n_be_intrastat/wizard/xml_decl.py:120
|
||||
#, python-format
|
||||
msgid "Year must be 4 digits number (YYYY)"
|
||||
msgstr ""
|
||||
|
||||
#. module: l10n_be_intrastat
|
||||
#: view:l10n_be_intrastat_xml.xml_decl:l10n_be_intrastat.view_intrastat_declaration_xml
|
||||
msgid "or"
|
||||
msgstr ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,203 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Business Applications
|
||||
# Copyright (C) 2014-2015 Odoo S.A. <http://www.odoo.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
class account_invoice(osv.osv):
|
||||
_inherit = "account.invoice"
|
||||
_columns = {
|
||||
'incoterm_id': fields.many2one(
|
||||
'stock.incoterms', 'Incoterm',
|
||||
help="International Commercial Terms are a series of predefined commercial terms "
|
||||
"used in international transactions."),
|
||||
'intrastat_transaction_id': fields.many2one(
|
||||
'l10n_be_intrastat.transaction', 'Intrastat Transaction Type',
|
||||
help="Intrastat nature of transaction"),
|
||||
'transport_mode_id': fields.many2one(
|
||||
'l10n_be_intrastat.transport_mode', 'Intrastat Transport Mode'),
|
||||
'intrastat_country_id': fields.many2one(
|
||||
'res.country', 'Intrastat Country',
|
||||
help='Intrastat country, delivery for sales, origin for purchases',
|
||||
domain=[('intrastat','=',True)]),
|
||||
}
|
||||
|
||||
|
||||
class intrastat_region(osv.osv):
|
||||
_name = 'l10n_be_intrastat.region'
|
||||
_columns = {
|
||||
'code': fields.char('Code', required=True),
|
||||
'country_id': fields.many2one('res.country', 'Country'),
|
||||
'name': fields.char('Name', translate=True),
|
||||
'description': fields.char('Description'),
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('l10n_be_intrastat_regioncodeunique', 'UNIQUE (code)', 'Code must be unique.'),
|
||||
]
|
||||
|
||||
|
||||
class intrastat_transaction(osv.osv):
|
||||
_name = 'l10n_be_intrastat.transaction'
|
||||
_rec_name = 'code'
|
||||
_columns = {
|
||||
'code': fields.char('Code', required=True, readonly=True),
|
||||
'description': fields.text('Description', readonly=True),
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('l10n_be_intrastat_trcodeunique', 'UNIQUE (code)', 'Code must be unique.'),
|
||||
]
|
||||
|
||||
|
||||
class intrastat_transport_mode(osv.osv):
|
||||
_name = 'l10n_be_intrastat.transport_mode'
|
||||
_columns = {
|
||||
'code': fields.char('Code', required=True, readonly=True),
|
||||
'name': fields.char('Description', readonly=True),
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('l10n_be_intrastat_trmodecodeunique', 'UNIQUE (code)', 'Code must be unique.'),
|
||||
]
|
||||
|
||||
|
||||
class product_category(osv.osv):
|
||||
_name = "product.category"
|
||||
_inherit = "product.category"
|
||||
|
||||
_columns = {
|
||||
'intrastat_id': fields.many2one('report.intrastat.code', 'Intrastat Code'),
|
||||
}
|
||||
|
||||
def get_intrastat_recursively(self, cr, uid, category, context=None):
|
||||
""" Recursively search in categories to find an intrastat code id
|
||||
|
||||
:param category : Browse record of a category
|
||||
"""
|
||||
if category.intrastat_id:
|
||||
res = category.intrastat_id.id
|
||||
elif category.parent_id:
|
||||
res = self.get_intrastat_recursively(cr, uid, category.parent_id, context=context)
|
||||
else:
|
||||
res = None
|
||||
return res
|
||||
|
||||
|
||||
class product_product(osv.osv):
|
||||
_name = "product.product"
|
||||
_inherit = "product.product"
|
||||
|
||||
def get_intrastat_recursively(self, cr, uid, id, context=None):
|
||||
""" Recursively search in categories to find an intrastat code id
|
||||
"""
|
||||
product = self.browse(cr, uid, id, context=context)
|
||||
if product.intrastat_id:
|
||||
res = product.intrastat_id.id
|
||||
elif product.categ_id:
|
||||
res = self.pool['product.category'].get_intrastat_recursively(
|
||||
cr, uid, product.categ_id, context=context)
|
||||
else:
|
||||
res = None
|
||||
return res
|
||||
|
||||
|
||||
class purchase_order(osv.osv):
|
||||
_inherit = "purchase.order"
|
||||
|
||||
def _prepare_invoice(self, cr, uid, order, line_ids, context=None):
|
||||
"""
|
||||
copy incoterm from purchase order to invoice
|
||||
"""
|
||||
invoice = super(purchase_order, self)._prepare_invoice(
|
||||
cr, uid, order, line_ids, context=context)
|
||||
if order.incoterm_id:
|
||||
invoice['incoterm_id'] = order.incoterm_id.id
|
||||
#Try to determine products origin
|
||||
if order.partner_id.country_id:
|
||||
#It comes from supplier
|
||||
invoice['intrastat_country_id'] = order.partner_id.country_id.id
|
||||
|
||||
return invoice
|
||||
|
||||
|
||||
class report_intrastat_code(osv.osv):
|
||||
_inherit = "report.intrastat.code"
|
||||
_columns = {
|
||||
'description': fields.text('Description', translate=True),
|
||||
}
|
||||
|
||||
|
||||
class res_company(osv.osv):
|
||||
_inherit = "res.company"
|
||||
_columns = {
|
||||
'region_id': fields.many2one('l10n_be_intrastat.region', 'Intrastat region'),
|
||||
'transport_mode_id': fields.many2one('l10n_be_intrastat.transport_mode',
|
||||
'Default transport mode'),
|
||||
'incoterm_id': fields.many2one('stock.incoterms', 'Default incoterm for Intrastat',
|
||||
help="International Commercial Terms are a series of "
|
||||
"predefined commercial terms used in international "
|
||||
"transactions."),
|
||||
}
|
||||
|
||||
|
||||
class sale_order(osv.osv):
|
||||
_inherit = "sale.order"
|
||||
|
||||
def _prepare_invoice(self, cr, uid, saleorder, lines, context=None):
|
||||
"""
|
||||
copy incoterm from sale order to invoice
|
||||
"""
|
||||
invoice = super(sale_order, self)._prepare_invoice(
|
||||
cr, uid, saleorder, lines, context=context)
|
||||
if saleorder.incoterm:
|
||||
invoice['incoterm_id'] = saleorder.incoterm.id
|
||||
# Guess products destination
|
||||
if saleorder.partner_shipping_id.country_id:
|
||||
invoice['intrastat_country_id'] = saleorder.partner_shipping_id.country_id.id
|
||||
elif saleorder.partner_id.country_id:
|
||||
invoice['intrastat_country_id'] = saleorder.partner_id.country_id.id
|
||||
elif saleorder.partner_invoice_id.country_id:
|
||||
invoice['intrastat_country_id'] = saleorder.partner_invoice_id.country_id.id
|
||||
return invoice
|
||||
|
||||
|
||||
class stock_warehouse(osv.osv):
|
||||
_inherit = "stock.warehouse"
|
||||
_columns = {
|
||||
'region_id': fields.many2one('l10n_be_intrastat.region', 'Intrastat region'),
|
||||
}
|
||||
|
||||
def get_regionid_from_locationid(self, cr, uid, location_id, context=None):
|
||||
location_model = self.pool['stock.location']
|
||||
|
||||
location = location_model.browse(cr, uid, location_id, context=context)
|
||||
location_ids = location_model.search(cr, uid,
|
||||
[('parent_left', '<=', location.parent_left),
|
||||
('parent_right', '>=', location.parent_right)],
|
||||
context=context)
|
||||
warehouse_ids = self.search(cr, uid,
|
||||
[('lot_stock_id', 'in', location_ids),
|
||||
('region_id', '!=', False)],
|
||||
context=context)
|
||||
warehouses = self.browse(cr, uid, warehouse_ids, context=context)
|
||||
if warehouses and warehouses[0]:
|
||||
return warehouses[0].region_id.id
|
||||
return None
|
||||
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- Customer Invoice Form -->
|
||||
<record id="l10n_be_intrastat_xml_decl_cust_invoice_view" model="ir.ui.view">
|
||||
<field name="name">l10n_be_intrastat_xml.custinvoice_form</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='date_due']" position="after">
|
||||
<field name="incoterm_id" groups="l10n_be_intrastat.intrastat_extended"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='move_id']" position="after">
|
||||
<field name="intrastat_transaction_id"/>
|
||||
<field name="transport_mode_id" groups="l10n_be_intrastat.intrastat_extended"/>
|
||||
<field name="intrastat_country_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
</record>
|
||||
|
||||
<!-- Transaction Form -->
|
||||
<record model="ir.ui.view" id="intra_view_transaction_form">
|
||||
<field name="name">intra.view.transaction_form</field>
|
||||
<field name="model">l10n_be_intrastat.transaction</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Intrastat transaction form">
|
||||
<group>
|
||||
<field name="code"/>
|
||||
<field name="description"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Transaction List -->
|
||||
<record model="ir.ui.view" id="intra_view_transaction_tree">
|
||||
<field name="name">intra.view.transaction_tree</field>
|
||||
<field name="model">l10n_be_intrastat.transaction</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Intrastat transaction types">
|
||||
<field name="code"/>
|
||||
<field name="description"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Transport mode Form -->
|
||||
<record model="ir.ui.view" id="intra_view_transport_mode_form">
|
||||
<field name="name">intra.view.transport_mode_form</field>
|
||||
<field name="model">l10n_be_intrastat.transport_mode</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Intrastat transportation mode form">
|
||||
<group>
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Transport mode List -->
|
||||
<record model="ir.ui.view" id="intra_view_transport_mode_tree">
|
||||
<field name="name">intra.view.transport_mode_tree</field>
|
||||
<field name="model">l10n_be_intrastat.transport_mode</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Intrastat transportation modes">
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Category Form -->
|
||||
<record id="l10n_be_intrastat_xml_decl_product_category_view" model="ir.ui.view">
|
||||
<field name="name">l10n_be_intrastat_xml.productcategory_form</field>
|
||||
<field name="model">product.category</field>
|
||||
<field name="inherit_id" ref="product.product_category_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='parent']" position="inside">
|
||||
<group string="Intrastat" colspan="2">
|
||||
<field name="intrastat_id"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Company -->
|
||||
<record id="l10n_be_intrastat_xml_decl_res_company_form_view" model="ir.ui.view">
|
||||
<field name="name">l10n_be_intrastat_xml.rescompany_form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='company_registry']" position="after">
|
||||
<field name="region_id" domain="[('country_id','=',country_id)]"/>
|
||||
<field name="transport_mode_id" groups="l10n_be_intrastat.intrastat_extended"/>
|
||||
<field name="incoterm_id" groups="l10n_be_intrastat.intrastat_extended"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Warehouse -->
|
||||
<record id="l10n_be_intrastat_xml_decl_stock_warehouse_form_view" model="ir.ui.view">
|
||||
<field name="name">l10n_be_intrastat_xml.stockwarehouse_form</field>
|
||||
<field name="model">stock.warehouse</field>
|
||||
<field name="inherit_id" ref="stock.view_warehouse"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="region_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
</record>
|
||||
|
||||
<!-- Supplier Invoice Form -->
|
||||
<record id="l10n_be_intrastat_xml_decl_supp_invoice_view" model="ir.ui.view">
|
||||
<field name="name">l10n_be_intrastat_xml.suppinvoice_form</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='payment_term']" position="after">
|
||||
<field name="incoterm_id" groups="l10n_be_intrastat.intrastat_extended"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='period_id']" position="after">
|
||||
<field name="intrastat_transaction_id" />
|
||||
<field name="transport_mode_id" groups="l10n_be_intrastat.intrastat_extended"/>
|
||||
<field name="intrastat_country_id" />
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="intrastat_extended" model="res.groups">
|
||||
<field name="name">Intrastat extended</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,4 @@
|
||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||
"access_l10n_be_intrastat_transaction","Intrastat transaction type","model_l10n_be_intrastat_transaction",,1,0,0,0
|
||||
"access_l10n_be_intrastat_transport_mode","Intrastat transport mode","model_l10n_be_intrastat_transport_mode",,1,0,0,0
|
||||
"access_l10n_be_intrastat_region","Intrastat regions","model_l10n_be_intrastat_region",,1,0,0,0
|
||||
|
@@ -0,0 +1,22 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Business Applications
|
||||
# Copyright (C) 2014-2015 Odoo S.A. <http://www.odoo.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import xml_decl
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="view_intrastat_declaration_xml" model="ir.ui.view">
|
||||
<field name="name">Intrastat XML Declaration</field>
|
||||
<field name="model">l10n_be_intrastat_xml.xml_decl</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="XML Intratstat Declaration">
|
||||
<!-- Need the field in the view in order to be able to hide or show part of the view according the state -->
|
||||
<field name="state" invisible="1" />
|
||||
<field name="name" invisible="1"/>
|
||||
<group states="draft">
|
||||
<group>
|
||||
<field name="month"/>
|
||||
<field name="year"/>
|
||||
<field name="tax_code_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="arrivals"/>
|
||||
<field name="dispatches"/>
|
||||
</group>
|
||||
</group>
|
||||
<div states="download">
|
||||
<h2>Intrastat Declaration</h2>
|
||||
<p>Here is the XML file with your intrastat declaration: <field name="file_save" filename="name"/></p>
|
||||
</div>
|
||||
<footer>
|
||||
<group states="draft">
|
||||
<button name="create_xml" string="Create XML" type="object" default_focus="1" class="oe_highlight" />
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</group>
|
||||
<group states="download">
|
||||
<button string="Close" special="cancel" />
|
||||
</group>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_intrastat_xml_declaration" model="ir.actions.act_window">
|
||||
<field name="name">Intrastat Declaration</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">l10n_be_intrastat_xml.xml_decl</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_intrastat_declaration_xml"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Intrastat Declaration"
|
||||
parent="l10n_be.menu_finance_belgian_statement"
|
||||
action="action_intrastat_xml_declaration"
|
||||
id="l10n_be_intrastat_xml_declaration"
|
||||
groups="account.group_account_manager"/>
|
||||
</data>
|
||||
|
||||
</openerp>
|
||||
@@ -0,0 +1,373 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Business Applications
|
||||
# Copyright (C) 2014-2015 Odoo S.A. <http://www.odoo.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import base64
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
|
||||
from openerp import exceptions, SUPERUSER_ID, tools
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
INTRASTAT_XMLNS = 'http://www.onegate.eu/2010-01-01'
|
||||
|
||||
class xml_decl(osv.TransientModel):
|
||||
"""
|
||||
Intrastat XML Declaration
|
||||
"""
|
||||
_name = "l10n_be_intrastat_xml.xml_decl"
|
||||
_description = 'Intrastat XML Declaration'
|
||||
|
||||
def _get_tax_code(self, cr, uid, context=None):
|
||||
obj_tax_code = self.pool.get('account.tax.code')
|
||||
obj_user = self.pool.get('res.users')
|
||||
company_id = obj_user.browse(cr, uid, uid, context=context).company_id.id
|
||||
tax_code_ids = obj_tax_code.search(cr, uid, [('company_id', '=', company_id),
|
||||
('parent_id', '=', False)],
|
||||
context=context)
|
||||
return tax_code_ids and tax_code_ids[0] or False
|
||||
|
||||
def _get_def_monthyear(self, cr, uid, context=None):
|
||||
td = datetime.strptime(fields.date.context_today(self, cr, uid, context=context),
|
||||
tools.DEFAULT_SERVER_DATE_FORMAT).date()
|
||||
return td.strftime('%Y'), td.strftime('%m')
|
||||
|
||||
def _get_def_month(self, cr, uid, context=None):
|
||||
return self._get_def_monthyear(cr, uid, context=context)[1]
|
||||
|
||||
def _get_def_year(self, cr, uid, context=None):
|
||||
return self._get_def_monthyear(cr, uid, context=context)[0]
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('File Name'),
|
||||
'month': fields.selection([('01','January'), ('02','February'), ('03','March'),
|
||||
('04','April'), ('05','May'), ('06','June'), ('07','July'),
|
||||
('08','August'), ('09','September'), ('10','October'),
|
||||
('11','November'), ('12','December')], 'Month', required=True),
|
||||
'year': fields.char('Year', size=4, required=True),
|
||||
'tax_code_id': fields.many2one('account.tax.code', 'Company Tax Chart',
|
||||
domain=[('parent_id', '=', False)], required=True),
|
||||
'arrivals': fields.selection([('be-exempt', 'Exempt'),
|
||||
('be-standard', 'Standard'),
|
||||
('be-extended', 'Extended')],
|
||||
'Arrivals', required=True),
|
||||
'dispatches': fields.selection([('be-exempt', 'Exempt'),
|
||||
('be-standard', 'Standard'),
|
||||
('be-extended', 'Extended')],
|
||||
'Dispatches', required=True),
|
||||
'file_save': fields.binary('Intrastat Report File', readonly=True),
|
||||
'state': fields.selection([('draft', 'Draft'), ('download', 'Download')], string="State"),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'arrivals': 'be-standard',
|
||||
'dispatches': 'be-standard',
|
||||
'name': 'intrastat.xml',
|
||||
'tax_code_id': _get_tax_code,
|
||||
'month': _get_def_month,
|
||||
'year': _get_def_year,
|
||||
'state': 'draft',
|
||||
}
|
||||
|
||||
def _company_warning(self, cr, uid, translated_msg, context=None):
|
||||
""" Raise a error with custom message, asking user to configure company settings """
|
||||
xmlid_mod = self.pool['ir.model.data']
|
||||
action_id = xmlid_mod.xmlid_to_res_id(cr, uid, 'base.action_res_company_form')
|
||||
raise exceptions.RedirectWarning(
|
||||
translated_msg, action_id, _('Go to company configuration screen'))
|
||||
|
||||
def create_xml(self, cr, uid, ids, context=None):
|
||||
"""Creates xml that is to be exported and sent to estate for partner vat intra.
|
||||
:return: Value for next action.
|
||||
:rtype: dict
|
||||
"""
|
||||
decl_datas = self.browse(cr, uid, ids[0])
|
||||
company = decl_datas.tax_code_id.company_id
|
||||
if not (company.partner_id and company.partner_id.country_id and
|
||||
company.partner_id.country_id.id):
|
||||
self._company_warning(
|
||||
cr, uid,
|
||||
_('The country of your company is not set, '
|
||||
'please make sure to configure it first.'),
|
||||
context=context)
|
||||
kbo = company.company_registry
|
||||
if not kbo:
|
||||
self._company_warning(
|
||||
cr, uid,
|
||||
_('The registry number of your company is not set, '
|
||||
'please make sure to configure it first.'),
|
||||
context=context)
|
||||
if len(decl_datas.year) != 4:
|
||||
raise exceptions.Warning(_('Year must be 4 digits number (YYYY)'))
|
||||
|
||||
#Create root declaration
|
||||
decl = ET.Element('DeclarationReport')
|
||||
decl.set('xmlns', INTRASTAT_XMLNS)
|
||||
|
||||
#Add Administration elements
|
||||
admin = ET.SubElement(decl, 'Administration')
|
||||
fromtag = ET.SubElement(admin, 'From')
|
||||
fromtag.text = kbo
|
||||
fromtag.set('declarerType', 'KBO')
|
||||
ET.SubElement(admin, 'To').text = "NBB"
|
||||
ET.SubElement(admin, 'Domain').text = "SXX"
|
||||
if decl_datas.arrivals == 'be-standard':
|
||||
decl.append(self._get_lines(cr, SUPERUSER_ID, ids, decl_datas, company,
|
||||
dispatchmode=False, extendedmode=False, context=context))
|
||||
elif decl_datas.arrivals == 'be-extended':
|
||||
decl.append(self._get_lines(cr, SUPERUSER_ID, ids, decl_datas, company,
|
||||
dispatchmode=False, extendedmode=True, context=context))
|
||||
if decl_datas.dispatches == 'be-standard':
|
||||
decl.append(self._get_lines(cr, SUPERUSER_ID, ids, decl_datas, company,
|
||||
dispatchmode=True, extendedmode=False, context=context))
|
||||
elif decl_datas.dispatches == 'be-extended':
|
||||
decl.append(self._get_lines(cr, SUPERUSER_ID, ids, decl_datas, company,
|
||||
dispatchmode=True, extendedmode=True, context=context))
|
||||
|
||||
#Get xml string with declaration
|
||||
data_file = ET.tostring(decl, encoding='UTF-8', method='xml')
|
||||
|
||||
#change state of the wizard
|
||||
self.write(cr, uid, ids,
|
||||
{'name': 'intrastat_%s%s.xml' % (decl_datas.year, decl_datas.month),
|
||||
'file_save': base64.encodestring(data_file),
|
||||
'state': 'download'},
|
||||
context=context)
|
||||
return {
|
||||
'name': _('Save'),
|
||||
'context': context,
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'l10n_be_intrastat_xml.xml_decl',
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'res_id': ids[0],
|
||||
}
|
||||
|
||||
def _get_lines(self, cr, uid, ids, decl_datas, company, dispatchmode=False,
|
||||
extendedmode=False, context=None):
|
||||
intrastatcode_mod = self.pool['report.intrastat.code']
|
||||
invoiceline_mod = self.pool['account.invoice.line']
|
||||
product_mod = self.pool['product.product']
|
||||
region_mod = self.pool['l10n_be_intrastat.region']
|
||||
warehouse_mod = self.pool['stock.warehouse']
|
||||
|
||||
if dispatchmode:
|
||||
mode1 = 'out_invoice'
|
||||
mode2 = 'in_refund'
|
||||
declcode = "29"
|
||||
else:
|
||||
mode1 = 'in_invoice'
|
||||
mode2 = 'out_refund'
|
||||
declcode = "19"
|
||||
|
||||
decl = ET.Element('Report')
|
||||
if not extendedmode:
|
||||
decl.set('code', 'EX%sS' % declcode)
|
||||
else:
|
||||
decl.set('code', 'EX%sE' % declcode)
|
||||
decl.set('date', '%s-%s' % (decl_datas.year, decl_datas.month))
|
||||
datas = ET.SubElement(decl, 'Data')
|
||||
if not extendedmode:
|
||||
datas.set('form', 'EXF%sS' % declcode)
|
||||
else:
|
||||
datas.set('form', 'EXF%sE' % declcode)
|
||||
datas.set('close', 'true')
|
||||
intrastatkey = namedtuple("intrastatkey",
|
||||
['EXTRF', 'EXCNT', 'EXTTA', 'EXREG',
|
||||
'EXGO', 'EXTPC', 'EXDELTRM'])
|
||||
entries = {}
|
||||
|
||||
sqlreq = """
|
||||
select
|
||||
inv_line.id
|
||||
from
|
||||
account_invoice_line inv_line
|
||||
join account_invoice inv on inv_line.invoice_id=inv.id
|
||||
left join res_country on res_country.id = inv.intrastat_country_id
|
||||
left join res_partner on res_partner.id = inv.partner_id
|
||||
left join res_country countrypartner on countrypartner.id = res_partner.country_id
|
||||
join product_product on inv_line.product_id=product_product.id
|
||||
join product_template on product_product.product_tmpl_id=product_template.id
|
||||
where
|
||||
inv.state in ('open','paid')
|
||||
and inv.company_id=%s
|
||||
and not product_template.type='service'
|
||||
and (res_country.intrastat=true or (inv.intrastat_country_id is null
|
||||
and countrypartner.intrastat=true))
|
||||
and ((res_country.code is not null and not res_country.code=%s)
|
||||
or (res_country.code is null and countrypartner.code is not null
|
||||
and not countrypartner.code=%s))
|
||||
and inv.type in (%s, %s)
|
||||
and to_char(inv.date_invoice, 'YYYY')=%s
|
||||
and to_char(inv.date_invoice, 'MM')=%s
|
||||
"""
|
||||
|
||||
cr.execute(sqlreq, (company.id, company.partner_id.country_id.code,
|
||||
company.partner_id.country_id.code, mode1, mode2,
|
||||
decl_datas.year, decl_datas.month))
|
||||
lines = cr.fetchall()
|
||||
invoicelines_ids = [rec[0] for rec in lines]
|
||||
invoicelines = invoiceline_mod.browse(cr, uid, invoicelines_ids, context=context)
|
||||
for inv_line in invoicelines:
|
||||
|
||||
#Check type of transaction
|
||||
if inv_line.invoice_id.intrastat_transaction_id:
|
||||
extta = inv_line.invoice_id.intrastat_transaction_id.code
|
||||
else:
|
||||
extta = "1"
|
||||
#Check country
|
||||
if inv_line.invoice_id.intrastat_country_id:
|
||||
excnt = inv_line.invoice_id.intrastat_country_id.code
|
||||
else:
|
||||
excnt = inv_line.invoice_id.partner_id.country_id.code
|
||||
|
||||
#Check region
|
||||
#If purchase, comes from purchase order, linked to a location,
|
||||
#which is linked to the warehouse
|
||||
#if sales, the sale order is linked to the warehouse
|
||||
#if sales, from a delivery order, linked to a location,
|
||||
#which is linked to the warehouse
|
||||
#If none found, get the company one.
|
||||
exreg = None
|
||||
if inv_line.invoice_id.type in ('in_invoice', 'in_refund'):
|
||||
#comes from purchase
|
||||
POL = self.pool['purchase.order.line']
|
||||
poline_ids = POL.search(
|
||||
cr, uid, [('invoice_lines', 'in', inv_line.id)], context=context)
|
||||
if poline_ids:
|
||||
purchaseorder = POL.browse(cr, uid, poline_ids[0], context=context).order_id
|
||||
region_id = warehouse_mod.get_regionid_from_locationid(
|
||||
cr, uid, purchaseorder.location_id.id, context=context)
|
||||
if region_id:
|
||||
exreg = region_mod.browse(cr, uid, region_id).code
|
||||
elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'):
|
||||
#comes from sales
|
||||
soline_ids = self.pool['sale.order.line'].search(
|
||||
cr, uid, [('invoice_lines', 'in', inv_line.id)], context=context)
|
||||
if soline_ids:
|
||||
saleorder = self.pool['sale.order.line'].browse(
|
||||
cr, uid, soline_ids[0], context=context).order_id
|
||||
if saleorder and saleorder.warehouse_id and saleorder.warehouse_id.region_id:
|
||||
exreg = region_mod.browse(
|
||||
cr, uid, saleorder.warehouse_id.region_id.id, context=context).code
|
||||
|
||||
if not exreg:
|
||||
if company.region_id:
|
||||
exreg = company.region_id.code
|
||||
else:
|
||||
self._company_warning(
|
||||
cr, uid,
|
||||
_('The Intrastat Region of the selected company is not set, '
|
||||
'please make sure to configure it first.'),
|
||||
context=context)
|
||||
|
||||
#Check commodity codes
|
||||
intrastat_id = product_mod.get_intrastat_recursively(
|
||||
cr, uid, inv_line.product_id.id, context=context)
|
||||
if intrastat_id:
|
||||
exgo = intrastatcode_mod.browse(cr, uid, intrastat_id, context=context).name
|
||||
else:
|
||||
raise exceptions.Warning(
|
||||
_('Product "%s" has no intrastat code, please configure it') %
|
||||
inv_line.product_id.display_name)
|
||||
|
||||
#In extended mode, 2 more fields required
|
||||
if extendedmode:
|
||||
#Check means of transport
|
||||
if inv_line.invoice_id.transport_mode_id:
|
||||
extpc = inv_line.invoice_id.transport_mode_id.code
|
||||
elif company.transport_mode_id:
|
||||
extpc = company.transport_mode_id.code
|
||||
else:
|
||||
self._company_warning(
|
||||
cr, uid,
|
||||
_('The default Intrastat transport mode of your company '
|
||||
'is not set, please make sure to configure it first.'),
|
||||
context=context)
|
||||
|
||||
#Check incoterm
|
||||
if inv_line.invoice_id.incoterm_id:
|
||||
exdeltrm = inv_line.invoice_id.incoterm_id.code
|
||||
elif company.incoterm_id:
|
||||
exdeltrm = company.incoterm_id.code
|
||||
else:
|
||||
self._company_warning(
|
||||
cr, uid,
|
||||
_('The default Incoterm of your company is not set, '
|
||||
'please make sure to configure it first.'),
|
||||
context=context)
|
||||
else:
|
||||
extpc = ""
|
||||
exdeltrm = ""
|
||||
linekey = intrastatkey(EXTRF=declcode, EXCNT=excnt,
|
||||
EXTTA=extta, EXREG=exreg, EXGO=exgo,
|
||||
EXTPC=extpc, EXDELTRM=exdeltrm)
|
||||
#We have the key
|
||||
#calculate amounts
|
||||
if inv_line.price_unit and inv_line.quantity:
|
||||
amount = inv_line.price_unit * inv_line.quantity
|
||||
else:
|
||||
amount = 0
|
||||
if (not inv_line.uos_id.category_id
|
||||
or not inv_line.product_id.uom_id.category_id
|
||||
or inv_line.uos_id.category_id.id != inv_line.product_id.uom_id.category_id.id):
|
||||
weight = inv_line.product_id.weight_net * inv_line.quantity
|
||||
else:
|
||||
weight = (inv_line.product_id.weight_net *
|
||||
inv_line.quantity * inv_line.uos_id.factor)
|
||||
if (not inv_line.uos_id.category_id or not inv_line.product_id.uom_id.category_id
|
||||
or inv_line.uos_id.category_id.id != inv_line.product_id.uom_id.category_id.id):
|
||||
supply_units = inv_line.quantity
|
||||
else:
|
||||
supply_units = inv_line.quantity * inv_line.uos_id.factor
|
||||
amounts = entries.setdefault(linekey, (0, 0, 0))
|
||||
amounts = (amounts[0] + amount, amounts[1] + weight, amounts[2] + supply_units)
|
||||
entries[linekey] = amounts
|
||||
|
||||
numlgn = 0
|
||||
for linekey in entries:
|
||||
numlgn += 1
|
||||
amounts = entries[linekey]
|
||||
item = ET.SubElement(datas, 'Item')
|
||||
self._set_Dim(item, 'EXSEQCODE', unicode(numlgn))
|
||||
self._set_Dim(item, 'EXTRF', unicode(linekey.EXTRF))
|
||||
self._set_Dim(item, 'EXCNT', unicode(linekey.EXCNT))
|
||||
self._set_Dim(item, 'EXTTA', unicode(linekey.EXTTA))
|
||||
self._set_Dim(item, 'EXREG', unicode(linekey.EXREG))
|
||||
self._set_Dim(item, 'EXTGO', unicode(linekey.EXGO))
|
||||
if extendedmode:
|
||||
self._set_Dim(item, 'EXTPC', unicode(linekey.EXTPC))
|
||||
self._set_Dim(item, 'EXDELTRM', unicode(linekey.EXDELTRM))
|
||||
self._set_Dim(item, 'EXTXVAL', unicode(round(amounts[0], 0)).replace(".", ","))
|
||||
self._set_Dim(item, 'EXWEIGHT', unicode(round(amounts[1], 0)).replace(".", ","))
|
||||
self._set_Dim(item, 'EXUNITS', unicode(round(amounts[2], 0)).replace(".", ","))
|
||||
|
||||
if numlgn == 0:
|
||||
#no datas
|
||||
datas.set('action', 'nihil')
|
||||
return decl
|
||||
|
||||
def _set_Dim(self, item, prop, value):
|
||||
dim = ET.SubElement(item, 'Dim')
|
||||
dim.set('prop', prop)
|
||||
dim.text = value
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<div>
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<td>
|
||||
@@ -44,16 +44,18 @@
|
||||
<span>Niveau :</span><br/>
|
||||
<span>Coef :</span><br/>
|
||||
<span>Entrée :</span><br/>
|
||||
<span>Sortie :</span><br/>
|
||||
<span>ORG. S.S :</span>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="o.employee_id.id or '_'"/><br/>
|
||||
<span t-esc="o.employee_id.ssnid or '_'"/><br/>
|
||||
<span t-esc="o.contract_id.job_id or '_'"/><br/>
|
||||
<span t-esc="o.contract_id.job_id.name or '_'"/><br/>
|
||||
<span t-esc="o.contract_id.qualif or '_'"/><br/>
|
||||
<span t-esc="o.contract_id.niveau or '_'"/><br/>
|
||||
<span t-esc="o.contract_id.coef or '_'"/><br/>
|
||||
<span t-esc="o.contract_id.date_start or '_'"/> Sortie :<br/>
|
||||
<span t-esc="o.contract_id.date_end or ''"/><br/>
|
||||
<span t-esc="o.contract_id.date_start or '_'"/><br/>
|
||||
<span t-esc="o.contract_id.date_end or '_'"/><br/>
|
||||
<span t-esc="o.employee_id.company_id.org_ss or '_'"/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -63,18 +65,22 @@
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Désignation</th>
|
||||
<th>Quantité / Base</th>
|
||||
<th>Taux</th>
|
||||
<th>Montant</th>
|
||||
<th colspan="2">Charges Patronales</th>
|
||||
<th>Taux Charges Patronales</th>
|
||||
<th>Montant Charges Patronales</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="get_payslip_lines(o.line_ids)" t-as="p">
|
||||
<td>
|
||||
<span t-esc="p.code or ''"/>
|
||||
<span t-esc="p.name or ''" class="col-xs-offset-1"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="p.name or ''"/>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="p.amount_select== 'percentage'">
|
||||
@@ -94,7 +100,9 @@
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="get_employer_line(o, p) and get_employer_line(o, p).rate or ''"/>
|
||||
<span t-esc="get_employer_line(o,p) and get_employer_line(o,p).total or ''"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="get_employer_line(o,p) and get_employer_line(o,p).total or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -396,8 +396,8 @@ class mail_compose_message(osv.TransientModel):
|
||||
values.setdefault('attachment_ids', list()).append(ir_attach_obj.create(cr, uid, data_attach, context=context))
|
||||
else:
|
||||
default_context = dict(context, default_composition_mode=composition_mode, default_model=model, default_res_id=res_id)
|
||||
default_values = self.default_get(cr, uid, ['composition_mode', 'model', 'res_id', 'subject', 'body', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'], context=default_context)
|
||||
values = dict((key, default_values[key]) for key in ['subject', 'body', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'] if key in default_values)
|
||||
default_values = self.default_get(cr, uid, ['composition_mode', 'model', 'res_id', 'parent_id', 'partner_ids', 'subject', 'body', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'], context=default_context)
|
||||
values = dict((key, default_values[key]) for key in ['subject', 'body', 'partner_ids', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'] if key in default_values)
|
||||
|
||||
if values.get('body_html'):
|
||||
values['body'] = values.pop('body_html')
|
||||
|
||||
@@ -800,6 +800,7 @@ class pos_order(osv.osv):
|
||||
picking_id = picking_obj.create(cr, uid, {
|
||||
'origin': order.name,
|
||||
'partner_id': addr.get('delivery',False),
|
||||
'date_done' : order.date_order,
|
||||
'picking_type_id': picking_type.id,
|
||||
'company_id': order.company_id.id,
|
||||
'move_type': 'direct',
|
||||
|
||||
@@ -244,7 +244,7 @@ FaceTime HD Camera, 1.2 MP Photos</field>
|
||||
<field name="uom_id" ref="product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||
<field name="description">Custom computer assembled on order based on customer's requirement.</field>
|
||||
<field name="default_code">B3423</field>
|
||||
<field name="default_code">B3424</field>
|
||||
</record>
|
||||
|
||||
<record id="product_product_6" model="product.product">
|
||||
|
||||
@@ -12,6 +12,7 @@ access_product_pricelist_type_user,product.pricelist.type.user,model_product_pri
|
||||
access_product_pricelist_user,product.pricelist.user,model_product_pricelist,base.group_user,1,0,0,0
|
||||
access_product_pricelist_version_user,product.pricelist.version.user,model_product_pricelist_version,base.group_user,1,0,0,0
|
||||
access_product_pricelist_item_user,product.pricelist.item.user,model_product_pricelist_item,base.group_user,1,0,0,0
|
||||
access_product_pricelist_item_sale_manager,product.pricelist.item salemanager,product.model_product_pricelist_item,base.group_sale_manager,1,1,1,1
|
||||
access_product_pricelist_type_partner_manager,product.pricelist.type partner manager,model_product_pricelist_type,base.group_partner_manager,1,0,0,0
|
||||
access_product_pricelist_partner_manager,product.pricelist partner manager,model_product_pricelist,base.group_partner_manager,1,0,0,0
|
||||
access_product_product_employee,product.product employee,model_product_product,base.group_user,1,0,0,0
|
||||
|
||||
|
@@ -60,6 +60,7 @@ class stock_move(osv.osv):
|
||||
def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
|
||||
if move.purchase_line_id:
|
||||
invoice_line_vals['purchase_line_id'] = move.purchase_line_id.id
|
||||
invoice_line_vals['account_analytic_id'] = move.purchase_line_id.account_analytic_id.id or False
|
||||
invoice_line_id = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
|
||||
if move.purchase_line_id:
|
||||
purchase_line = move.purchase_line_id
|
||||
|
||||
@@ -39,6 +39,7 @@ class res_country(osv.osv):
|
||||
class report_intrastat_code(osv.osv):
|
||||
_name = "report.intrastat.code"
|
||||
_description = "Intrastat code"
|
||||
_translate = False
|
||||
_columns = {
|
||||
'name': fields.char('Intrastat Code'),
|
||||
'description': fields.char('Description'),
|
||||
|
||||
@@ -99,7 +99,7 @@ class crm_make_sale(osv.osv_memory):
|
||||
'pricelist_id': pricelist,
|
||||
'partner_invoice_id': partner_addr['invoice'],
|
||||
'partner_shipping_id': partner_addr['delivery'],
|
||||
'date_order': fields.date.context_today(self,cr,uid,context=context),
|
||||
'date_order': fields.datetime.now(),
|
||||
'fiscal_position': fpos,
|
||||
'payment_term':payment_term,
|
||||
'opportunity_id': case.id,
|
||||
|
||||
@@ -44,6 +44,7 @@ synchronization with other companies.
|
||||
'website': 'https://www.odoo.com',
|
||||
'data': [
|
||||
'security/share_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'res_users_view.xml',
|
||||
'wizard/share_wizard_view.xml',
|
||||
'views/share.xml',
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
"access_ir_ui_menu_group_shared","ir_ui_menu group_shared","base.model_ir_ui_menu",group_shared,1,0,0,0
|
||||
|
@@ -6,5 +6,13 @@
|
||||
<field name="parent_id" ref="base.module_category_tools"/>
|
||||
<field name="sequence">26</field>
|
||||
</record>
|
||||
|
||||
<record id="group_shared" model="res.groups">
|
||||
<field name="name">Shared Group</field>
|
||||
<field name="comment">
|
||||
This group is a template group for shared users, giving basic access needs, the menus for instance.</field>
|
||||
<field name="category_id" ref="module_category_share"/>
|
||||
<field name="share">1</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -7,5 +7,13 @@
|
||||
<script type="text/javascript" src="/share/static/src/js/share.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
<template id="webclient_bootstrap" name="Webclient Bootstrap - Share" inherit_id="web.webclient_bootstrap">
|
||||
<xpath expr="//nav[@id='oe_main_menu_navbar']" position="attributes">
|
||||
<attribute name="groups">base.group_user,base.group_portal,share.group_shared</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@class='openerp openerp_webclient_container']//div" position="attributes">
|
||||
<attribute name="groups">base.group_user,base.group_portal,share.group_shared</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -212,8 +212,15 @@ class share_wizard(osv.TransientModel):
|
||||
def _create_share_group(self, cr, uid, wizard_data, context=None):
|
||||
group_obj = self.pool.get('res.groups')
|
||||
share_group_name = '%s: %s (%d-%s)' %('Shared', wizard_data.name, uid, time.time())
|
||||
values = {'name': share_group_name, 'share': True}
|
||||
try:
|
||||
implied_group_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'group_shared')[1]
|
||||
except ValueError:
|
||||
implied_group_id = None
|
||||
if implied_group_id:
|
||||
values['implied_ids'] = [(4, implied_group_id)]
|
||||
# create share group without putting admin in it
|
||||
return group_obj.create(cr, UID_ROOT, {'name': share_group_name, 'share': True}, {'noadmin': True})
|
||||
return group_obj.create(cr, UID_ROOT, values, {'noadmin': True})
|
||||
|
||||
def _create_new_share_users(self, cr, uid, wizard_data, group_id, context=None):
|
||||
"""Create one new res.users record for each email address provided in
|
||||
@@ -486,7 +493,7 @@ class share_wizard(osv.TransientModel):
|
||||
# already granted
|
||||
for dummy, model in fields_relations:
|
||||
# mail.message is transversal: it should not received directly the access rights
|
||||
if model.model in ['mail.message']: continue
|
||||
if model.model in ['mail.message', 'mail.notification']: continue
|
||||
values = {
|
||||
'name': _('Copied access for sharing'),
|
||||
'group_id': group_id,
|
||||
@@ -618,7 +625,7 @@ class share_wizard(osv.TransientModel):
|
||||
if domain:
|
||||
for rel_field, model in fields_relations:
|
||||
# mail.message is transversal: it should not received directly the access rights
|
||||
if model.model in ['mail.message']: continue
|
||||
if model.model in ['mail.message', 'mail.notification']: continue
|
||||
related_domain = []
|
||||
if not rel_field: continue
|
||||
for element in domain:
|
||||
@@ -891,8 +898,8 @@ class share_result_line(osv.osv_memory):
|
||||
for this in self.browse(cr, uid, ids, context=context):
|
||||
data = dict(dbname=cr.dbname, login=this.login, password=this.password)
|
||||
if this.share_wizard_id and this.share_wizard_id.action_id:
|
||||
data['action_id'] = this.share_wizard_id.action_id.id
|
||||
this = this.with_context(share_url_template_hash_arguments=['action_id'])
|
||||
data['action'] = this.share_wizard_id.action_id.id
|
||||
this = this.with_context(share_url_template_hash_arguments=['action'])
|
||||
result[this.id] = this.share_wizard_id.share_url_template() % data
|
||||
return result
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ access_stock_lines_date_user,report.stock.lines.date user,model_report_stock_lin
|
||||
access_stock_lines_date_manager,report.stock.lines.date manager,model_report_stock_lines_date,stock.group_stock_manager,1,1,1,1
|
||||
access_product_product_stock_user,product_product_stock_user,product.model_product_product,stock.group_stock_user,1,1,0,0
|
||||
access_product_template_stock_user,product.template stock user,product.model_product_template,stock.group_stock_user,1,1,0,0
|
||||
access_product_pricelist_item_sale_manager,product.pricelist.item salemanager,product.model_product_pricelist_item,base.group_sale_manager,1,1,1,1
|
||||
access_product_uom_categ_stock_manager,product.uom.categ stock_manager,product.model_product_uom_categ,stock.group_stock_manager,1,1,1,1
|
||||
access_product_uom_stock_manager,product.uom stock_manager,product.model_product_uom,stock.group_stock_manager,1,1,1,1
|
||||
access_product_ul_stock_manager,product.ul stock_manager,product.model_product_ul,stock.group_stock_manager,1,1,1,1
|
||||
|
||||
|
+35
-27
@@ -420,10 +420,12 @@ class stock_quant(osv.osv):
|
||||
self._quant_reconcile_negative(cr, uid, quant, move, context=context)
|
||||
|
||||
def move_quants_write(self, cr, uid, quants, move, location_dest_id, dest_package_id, context=None):
|
||||
context=context or {}
|
||||
vals = {'location_id': location_dest_id.id,
|
||||
'history_ids': [(4, move.id)],
|
||||
'package_id': dest_package_id,
|
||||
'reservation_id': False}
|
||||
if not context.get('entire_pack'):
|
||||
vals.update({'package_id': dest_package_id})
|
||||
self.write(cr, SUPERUSER_ID, [q.id for q in quants], vals, context=context)
|
||||
|
||||
def quants_get_prefered_domain(self, cr, uid, location, product, qty, domain=None, prefered_domain_list=[], restrict_lot_id=False, restrict_partner_id=False, context=None):
|
||||
@@ -1061,7 +1063,7 @@ class stock_picking(osv.osv):
|
||||
'product_qty': 1.0,
|
||||
'location_id': pack.location_id.id,
|
||||
'location_dest_id': quants_suggested_locations[pack_quants[0]],
|
||||
'owner_id': picking.owner_id.id,
|
||||
'owner_id': pack.owner_id.id,
|
||||
})
|
||||
#remove the quants inside the package so that they are excluded from the rest of the computation
|
||||
for quant in pack_quants:
|
||||
@@ -2359,16 +2361,17 @@ class stock_move(osv.osv):
|
||||
dom = main_domain + self.pool.get('stock.move.operation.link').get_specific_domain(cr, uid, record, context=context)
|
||||
quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id, record.qty, domain=dom, prefered_domain_list=prefered_domain_list,
|
||||
restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
|
||||
if ops.result_package_id.id:
|
||||
#if a result package is given, all quants go there
|
||||
quant_dest_package_id = ops.result_package_id.id
|
||||
elif ops.product_id and ops.package_id:
|
||||
#if a package and a product is given, we will remove quants from the pack.
|
||||
quant_dest_package_id = False
|
||||
if ops.product_id:
|
||||
#If a product is given, the result is always put immediately in the result package (if it is False, they are without package)
|
||||
quant_dest_package_id = ops.result_package_id.id
|
||||
ctx = context
|
||||
else:
|
||||
#otherwise we keep the current pack of the quant, which may mean None
|
||||
quant_dest_package_id = ops.package_id.id
|
||||
quant_obj.quants_move(cr, uid, quants, move, ops.location_dest_id, location_from=ops.location_id, lot_id=ops.lot_id.id, owner_id=ops.owner_id.id, src_package_id=ops.package_id.id, dest_package_id=quant_dest_package_id, context=context)
|
||||
# When a pack is moved entirely, the quants should not be written anything for the destination package
|
||||
quant_dest_package_id = False
|
||||
ctx = context.copy()
|
||||
ctx['entire_pack'] = True
|
||||
quant_obj.quants_move(cr, uid, quants, move, ops.location_dest_id, location_from=ops.location_id, lot_id=ops.lot_id.id, owner_id=ops.owner_id.id, src_package_id=ops.package_id.id, dest_package_id=quant_dest_package_id, context=ctx)
|
||||
|
||||
# Handle pack in pack
|
||||
if not ops.product_id and ops.package_id and ops.result_package_id.id != ops.package_id.parent_id.id:
|
||||
self.pool.get('stock.quant.package').write(cr, SUPERUSER_ID, [ops.package_id.id], {'parent_id': ops.result_package_id.id}, context=context)
|
||||
@@ -3711,10 +3714,29 @@ class stock_package(osv.osv):
|
||||
"""Returns packages from quants for store"""
|
||||
res = set()
|
||||
for quant in self.browse(cr, uid, ids, context=context):
|
||||
if quant.package_id:
|
||||
res.add(quant.package_id.id)
|
||||
pack = quant.package_id
|
||||
while pack:
|
||||
res.add(pack.id)
|
||||
pack = pack.parent_id
|
||||
return list(res)
|
||||
|
||||
def _get_package_info(self, cr, uid, ids, name, args, context=None):
|
||||
quant_obj = self.pool.get("stock.quant")
|
||||
default_company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
|
||||
res = dict((res_id, {'location_id': False, 'company_id': default_company_id, 'owner_id': False}) for res_id in ids)
|
||||
for pack in self.browse(cr, uid, ids, context=context):
|
||||
quants = quant_obj.search(cr, uid, [('package_id', 'child_of', pack.id)], context=context)
|
||||
if quants:
|
||||
quant = quant_obj.browse(cr, uid, quants[0], context=context)
|
||||
res[pack.id]['location_id'] = quant.location_id.id
|
||||
res[pack.id]['owner_id'] = quant.owner_id.id
|
||||
res[pack.id]['company_id'] = quant.company_id.id
|
||||
else:
|
||||
res[pack.id]['location_id'] = False
|
||||
res[pack.id]['owner_id'] = False
|
||||
res[pack.id]['company_id'] = False
|
||||
return res
|
||||
|
||||
def _get_packages_to_relocate(self, cr, uid, ids, context=None):
|
||||
res = set()
|
||||
for pack in self.browse(cr, uid, ids, context=context):
|
||||
@@ -3723,20 +3745,6 @@ class stock_package(osv.osv):
|
||||
res.add(pack.parent_id.id)
|
||||
return list(res)
|
||||
|
||||
def _get_package_info(self, cr, uid, ids, name, args, context=None):
|
||||
default_company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
|
||||
res = dict((res_id, {'location_id': False, 'company_id': default_company_id, 'owner_id': False}) for res_id in ids)
|
||||
for pack in self.browse(cr, uid, ids, context=context):
|
||||
if pack.quant_ids:
|
||||
res[pack.id]['location_id'] = pack.quant_ids[0].location_id.id
|
||||
res[pack.id]['owner_id'] = pack.quant_ids[0].owner_id and pack.quant_ids[0].owner_id.id or False
|
||||
res[pack.id]['company_id'] = pack.quant_ids[0].company_id.id
|
||||
elif pack.children_ids:
|
||||
res[pack.id]['location_id'] = pack.children_ids[0].location_id and pack.children_ids[0].location_id.id or False
|
||||
res[pack.id]['owner_id'] = pack.children_ids[0].owner_id and pack.children_ids[0].owner_id.id or False
|
||||
res[pack.id]['company_id'] = pack.children_ids[0].company_id and pack.children_ids[0].company_id.id or False
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Package Reference', select=True, copy=False),
|
||||
'complete_name': fields.function(_complete_name, type='char', string="Package Name",),
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="location_id"/>
|
||||
<field name="location_id" domain="[('usage','not in', ['supplier','production'])]"/>
|
||||
<field name="filter" string="Inventory of" widget='radio' attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
</group>
|
||||
<group>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from . import test_invoicing
|
||||
@@ -0,0 +1,60 @@
|
||||
# Author: Leonardo Pistone
|
||||
# Copyright 2015 Camptocamp SA
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from openerp.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestCreateInvoice(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCreateInvoice, self).setUp()
|
||||
self.Wizard = self.env['stock.invoice.onshipping']
|
||||
|
||||
self.customer = self.env.ref('base.res_partner_3')
|
||||
product = self.env.ref('product.product_product_36')
|
||||
dropship_route = self.env.ref('stock_dropshipping.route_drop_shipping')
|
||||
|
||||
self.so = self.env['sale.order'].create({
|
||||
'partner_id': self.customer.id,
|
||||
})
|
||||
self.sol = self.env['sale.order.line'].create({
|
||||
'name': '/',
|
||||
'order_id': self.so.id,
|
||||
'product_id': product.id,
|
||||
'route_id': dropship_route.id,
|
||||
})
|
||||
|
||||
def test_po_on_delivery_creates_correct_invoice(self):
|
||||
self.so.action_button_confirm()
|
||||
|
||||
po = self.so.procurement_group_id.procurement_ids.purchase_id
|
||||
self.assertTrue(po)
|
||||
po.invoice_method = 'picking'
|
||||
po.signal_workflow('purchase_confirm')
|
||||
|
||||
picking = po.picking_ids
|
||||
self.assertEqual(1, len(picking))
|
||||
picking.action_done()
|
||||
|
||||
wizard = self.Wizard.with_context({
|
||||
'active_id': picking.id,
|
||||
'active_ids': [picking.id],
|
||||
}).create({})
|
||||
invoice_ids = wizard.create_invoice()
|
||||
invoices = self.env['account.invoice'].browse(invoice_ids)
|
||||
self.assertEqual(1, len(invoices))
|
||||
self.assertEqual(invoices.type, 'in_invoice')
|
||||
self.assertEqual(invoices, po.invoice_ids)
|
||||
@@ -1255,7 +1255,7 @@ class Action(http.Controller):
|
||||
except Exception:
|
||||
action_id = 0 # force failed read
|
||||
|
||||
base_action = Actions.read([action_id], ['type'], request.context)
|
||||
base_action = Actions.read([action_id], ['name', 'type'], request.context)
|
||||
if base_action:
|
||||
ctx = request.context
|
||||
action_type = base_action[0]['type']
|
||||
@@ -1265,7 +1265,7 @@ class Action(http.Controller):
|
||||
ctx.update(additional_context)
|
||||
action = request.session.model(action_type).read([action_id], False, ctx)
|
||||
if action:
|
||||
value = clean_action(action[0])
|
||||
value = clean_action(dict(action[0], **base_action[0]))
|
||||
return value
|
||||
|
||||
@http.route('/web/action/run', type='json', auth="user")
|
||||
|
||||
@@ -177,7 +177,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
|
||||
return value.format(date_format + ' ' + time_format);
|
||||
case 'date':
|
||||
if (typeof(value) == "string")
|
||||
value = moment(instance.web.str_to_date(value.substring(0,10)));
|
||||
value = moment(instance.web.auto_str_to_date(value));
|
||||
else {
|
||||
value = moment(value);
|
||||
}
|
||||
|
||||
@@ -478,7 +478,7 @@ openerp.PropertiesMixin = _.extend({}, openerp.EventDispatcherMixin, {
|
||||
var tmp = self.__getterSetterInternalMap[key];
|
||||
if (tmp === val)
|
||||
return;
|
||||
if (self.field && self.field.type === 'float' && tmp && val){
|
||||
if (key === 'value' && self.field && self.field.type === 'float' && tmp && val){
|
||||
var precision = self.field.digits ? self.field.digits[1] : 2;
|
||||
if (openerp.web.float_is_zero(tmp - val, precision))
|
||||
return;
|
||||
|
||||
@@ -448,8 +448,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||
return "";
|
||||
};
|
||||
|
||||
self._onchange_fields = [];
|
||||
self._onchange_specs = {};
|
||||
_.each(this.fields, function(field, name) {
|
||||
self._onchange_fields.push(name);
|
||||
self._onchange_specs[name] = find(name, field.node);
|
||||
_.each(field.field.views, function(view) {
|
||||
_.each(view.fields, function(_, subname) {
|
||||
@@ -486,7 +488,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||
var change_spec = widget ? onchange_specs[widget.name] : null;
|
||||
if (!widget || (!_.isEmpty(change_spec) && change_spec !== "0")) {
|
||||
var ids = [],
|
||||
trigger_field_name = widget ? widget.name : false,
|
||||
trigger_field_name = widget ? widget.name : self._onchange_fields,
|
||||
values = self._get_onchange_values(),
|
||||
context = new instance.web.CompoundContext(self.dataset.get_context());
|
||||
|
||||
@@ -1157,7 +1159,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||
},
|
||||
build_eval_context: function() {
|
||||
var a_dataset = this.dataset;
|
||||
return new instance.web.CompoundContext(this._build_view_fields_values(), a_dataset.get_context());
|
||||
return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values());
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -356,7 +356,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
||||
//Sort
|
||||
var default_order = this.fields_view.arch.attrs.default_order,
|
||||
unsorted = !this.dataset._sort.length;
|
||||
if (unsorted && default_order) {
|
||||
if (unsorted && default_order && !this.grouped) {
|
||||
this.dataset.set_sort(default_order.split(','));
|
||||
}
|
||||
|
||||
@@ -2230,7 +2230,7 @@ instance.web.list.Column = instance.web.Class.extend({
|
||||
}
|
||||
if (attrs.invisible) { return ''; }
|
||||
|
||||
if (!(row_data[this.id] && row_data[this.id].value)) {
|
||||
if (!row_data[this.id]) {
|
||||
return options.value_if_empty === undefined
|
||||
? ''
|
||||
: options.value_if_empty;
|
||||
@@ -2299,8 +2299,12 @@ instance.web.list.Binary = instance.web.list.Column.extend({
|
||||
_format: function (row_data, options) {
|
||||
var text = _t("Download");
|
||||
var value = row_data[this.id].value;
|
||||
if (!value) {
|
||||
return options.value_if_empty || '';
|
||||
}
|
||||
|
||||
var download_url;
|
||||
if (value && value.substr(0, 10).indexOf(' ') == -1) {
|
||||
if (value.substr(0, 10).indexOf(' ') == -1) {
|
||||
download_url = "data:application/octet-stream;base64," + value;
|
||||
} else {
|
||||
download_url = instance.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id});
|
||||
|
||||
@@ -841,6 +841,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
||||
if (_.isString(groupby)) {
|
||||
groupby = [groupby];
|
||||
}
|
||||
if (!controller.grouped && !_.isEmpty(groupby)){
|
||||
self.dataset.set_sort([]);
|
||||
}
|
||||
$.when(controller.do_search(results.domain, results.context, groupby || [])).then(function() {
|
||||
self.active_search.resolve();
|
||||
});
|
||||
|
||||
@@ -60,6 +60,9 @@ class contactus(http.Controller):
|
||||
post_description = [] # Info to add after the message
|
||||
values = {}
|
||||
|
||||
values['medium_id'] = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, SUPERUSER_ID, 'crm.crm_medium_website')
|
||||
values['team_id'] = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, SUPERUSER_ID, 'website.salesteam_website_sales')
|
||||
|
||||
for field_name, field_value in kwargs.items():
|
||||
if hasattr(field_value, 'filename'):
|
||||
post_file.append(field_value)
|
||||
@@ -77,9 +80,6 @@ class contactus(http.Controller):
|
||||
values = dict(values, error=error, kwargs=kwargs.items())
|
||||
return request.website.render(kwargs.get("view_from", "website.contactus"), values)
|
||||
|
||||
values['medium_id'] = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, SUPERUSER_ID, 'crm.crm_medium_website')
|
||||
values['team_id'] = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, SUPERUSER_ID, 'website.salesteam_website_sales')
|
||||
|
||||
# description is required, so it is always already initialized
|
||||
if post_description:
|
||||
values['description'] += dict_to_str(_("Custom Fields: "), post_description)
|
||||
|
||||
@@ -53,14 +53,14 @@ class WebsiteCustomer(http.Controller):
|
||||
|
||||
if country_id:
|
||||
domain += [('country_id', '=', country_id)]
|
||||
if not any(x['country_id'][0] == country_id for x in countries):
|
||||
if not any(x['country_id'][0] == country_id for x in countries if x['country_id']):
|
||||
country = country_obj.read(cr, uid, country_id, ['name'], context)
|
||||
if country:
|
||||
countries.append({
|
||||
'country_id_count': 0,
|
||||
'country_id': (country_id, country['name'])
|
||||
})
|
||||
countries.sort(key=lambda d: d['country_id'][1])
|
||||
countries.sort(key=lambda d: d['country_id'] and d['country_id'][1])
|
||||
curr_country = country_obj.browse(cr, uid, country_id, context)
|
||||
|
||||
countries.insert(0, {
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
},
|
||||
{
|
||||
title: "Check that the cart contains exactly 3 elements",
|
||||
element: '#top_menu .my_cart_quantity:containsExact(3)',
|
||||
element: 'a:has(.my_cart_quantity):containsExact(3)',
|
||||
},
|
||||
{
|
||||
title: "Modify the cart to add 1 unit of `VIP` ticket type",
|
||||
@@ -58,7 +58,7 @@
|
||||
},
|
||||
{
|
||||
title: "Now click on `Process Checkout`",
|
||||
waitFor: '#top_menu .my_cart_quantity:contains(4)',
|
||||
waitFor: 'a:has(.my_cart_quantity):contains(4)',
|
||||
element: '.btn-primary:contains("Process Checkout")'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -852,6 +852,9 @@ class website_sale(http.Controller):
|
||||
composer_values.update(composer_obj.onchange_template_id(cr, SUPERUSER_ID, None, *template_values, context=context).get('value', {}))
|
||||
if not composer_values.get('email_from') and uid == request.website.user_id.id:
|
||||
composer_values['email_from'] = request.website.user_id.company_id.email
|
||||
for key in ['attachment_ids', 'partner_ids']:
|
||||
if composer_values.get(key):
|
||||
composer_values[key] = [(6, 0, composer_values[key])]
|
||||
composer_id = composer_obj.create(cr, SUPERUSER_ID, composer_values, context=email_ctx)
|
||||
composer_obj.send_mail(cr, SUPERUSER_ID, [composer_id], context=email_ctx)
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Odoo Corporate Contributor License Agreement
|
||||
## Odoo CCLA v1.0
|
||||
Based on the Apache Software Foundation Corporate Contributor License Agreement v2.0, with modifications
|
||||
|
||||
Thank you for your interest in an Odoo S.A. (the "Project Leads") open source project. In order to clarify the intellectual property license granted with Contributions from any person or entity, the Odoo Project Leads must have a Contributor License Agreement (the "Agreement") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of the Project, its users, and the Odoo Project Leads; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete, sign and send the Agreement by:
|
||||
|
||||
* Scanning and emailing to cla-submission@odoo.com
|
||||
|
||||
**Please read this document carefully before signing and keep a copy for your records.**
|
||||
|
||||
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Project. Except for the license granted herein to the Project Leads and recipients of software distributed by the Project Leads, You reserve all right, title, and interest in and to Your Contributions.
|
||||
|
||||
1. Definitions.
|
||||
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
"Contribution" shall mean the code, documentation or any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to the Project Leads for inclusion in, or documentation of, any of the products managed or maintained by the Project Leads (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Project Leads or their representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Project Leads for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project Leads and to recipients of software distributed by the Project Leads a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project Leads and to recipients of software distributed by the Project Leads a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation.
|
||||
|
||||
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
|
||||
|
||||
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
7. Should You wish to submit work that is not Your original creation, You may submit it to the Project Leads separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
|
||||
|
||||
8. It is your responsibility to notify the Project Leads when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with the Project.
|
||||
|
||||
|
||||
```
|
||||
Corporation name: __________________________________________
|
||||
Corporation address: _______________________________________
|
||||
_______________________________________
|
||||
Country: ___________________________________________________
|
||||
Point of contact: __________________________________________
|
||||
Title: _____________________________________________________
|
||||
Email: _____________________________________________________
|
||||
Telephone: _________________________________________________
|
||||
Signature: _________________________________________________
|
||||
Date: ______________________________________________________
|
||||
```
|
||||
|
||||
## Schedule A
|
||||
[Initial list of designated employees. NB: authorization is not tied to particular Contributions.]
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
India, 2015-02-10
|
||||
|
||||
Serpent Consulting Services Pvt Ltd. agrees to the terms of the Odoo Corporate Contributor License Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Serpent Consulting Services Pvt Ltd. contact@serpentcs.com https://github.com/JayVora-SerpentCS
|
||||
|
||||
List of contributors:
|
||||
|
||||
Jay Vora jay.vora@serpentcs.com https://github.com/JayVora-SerpentCS
|
||||
Husen Daudi husen.daudi@serpentcs.com https://github.com/hda
|
||||
@@ -0,0 +1,24 @@
|
||||
Belgium, 2015-02-11
|
||||
|
||||
ACSONE SA/NV agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Stéphane Bidoul stephane.bidoul@acsone.eu https://github.com/sbidoul
|
||||
|
||||
List of contributors:
|
||||
|
||||
Adrien Peiffer adrien.peiffer@acsone.eu https://github.com/adrienpeiffer
|
||||
Anthony Muschang anthony.muschang@acsone.eu https://github.com/anthony-muschang
|
||||
Cédric Pigeon cedric.pigeon@acsone.eu https://github.com/Cedric-Pigeon
|
||||
Christelle De Coninck christelle.deconinck@acsone.eu https://github.com/chrisdec
|
||||
Jonathan Nemry jonathan.nemry@acsone.eu https://github.com/JonathanNEMRY
|
||||
Laetia Gangloff laetitia.gangloff@acsone.eu https://github.com/laetitia-gangloff
|
||||
Laurent Mignon laurent.mignon@acsone.eu https://github.com/lmignon
|
||||
Olivier Laurent olivier.laurent@acsone.eu https://github.com/Olivier-LAURENT
|
||||
Philippe Schmidt philippe.schmidt@acsone.eu https://github.com/phschmidt
|
||||
Stéphane Bidoul stephane.bidoul@acsone.eu https://github.com/sbidoul
|
||||
@@ -0,0 +1,16 @@
|
||||
Netherlands, 8-2-2015
|
||||
|
||||
BAS Solutions agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Erwin van der Ploeg erwin@bas-solutions.nl https://github.com/erwin-bas-solutions
|
||||
|
||||
List of contributors:
|
||||
|
||||
Erwin van der Ploeg erwin@bas-solutions.nl https://github.com/erwin-bas-solutions
|
||||
Ruchir Shukla ruchir@bas-solutions.nl https://github.com/ruchir-bas-solutions
|
||||
@@ -0,0 +1,28 @@
|
||||
Switzerland, France, 2015-02-10
|
||||
|
||||
Camptocamp SA and Camptocamp SaS agrees to the terms of the Odoo Corporate
|
||||
Contributor License Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Luc Maurer luc.maurer@camptocamp.com https://github.com/camptocamp
|
||||
|
||||
List of contributors:
|
||||
|
||||
Luc Maurer luc.maurer@camptocamp.com https://github.com/lucmaurer
|
||||
Joël Grand-Guillaume joel.grandguillaume@camptocamp.com https://github.com/jgrandguillaume
|
||||
Guewen Baconnier guewen.baconnier@camptocamp.com https://github.com/guewen
|
||||
Yannick Vaucher yannick.vaucher@camptocamp.com https://github.com/yvaucher
|
||||
Nicolas Bessi nicolas.bessi@camptocamp.com https://github.com/nbessi
|
||||
Frédéric Clementi frederic.clementi@camptocamp.com https://github.com/fclementic2c
|
||||
Laurent Meuwly laurent.meuwly@camptocamp.com https://github.com/laurentmeuwly
|
||||
Vincent Renaville vincet.renaville@camptocamp.com https://github.com/vrenaville
|
||||
Matthieu Dietrich matthieu.dietrich@camptocamp.com https://github.com/mdietrichc2c
|
||||
Alexandre Fayolle alexandre.fayolle@camptocamp.com https://github.com/gurneyalex
|
||||
Leonardo Pistone leonardo.pistone@camptocamp.com https://github.com/lepistone
|
||||
Romain Deheele romain.deheele@camptocamp.com https://github.com/rdeheele
|
||||
Maxime Wiot maxime.wiot@camptocamp.com https://github.com/maxime-c2c
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
Germany 2015-02-11
|
||||
|
||||
copado MEDIA UG agrees to the terms of the Odoo Corporate
|
||||
Contributor License Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Mathias Neef mn@copado.de https://github.com/copado
|
||||
|
||||
List of contributors:
|
||||
|
||||
Mathias Neef mn@copado.de https://github.com/copmn
|
||||
@@ -0,0 +1,16 @@
|
||||
Hungary, 2015 febr. 09
|
||||
|
||||
i3 Rendszerház Kft. agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Csaba Tóth csaba.toth@i3rendszerhaz.hu https://github.com/tsabi
|
||||
|
||||
List of contributors:
|
||||
|
||||
Csaba Tóth csaba.toth@i3rendszerhaz.hu https://github.com/tsabi
|
||||
Anna Szabó anna.szabo@i3rendszerhaz.hu
|
||||
@@ -0,0 +1,29 @@
|
||||
Canada, France, 8-2-2015
|
||||
|
||||
Savoir-faire Linux agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Maxime Chambreuil partners@savoirfairelinux.com https://github.com/savoirfairelinux
|
||||
|
||||
List of contributors:
|
||||
|
||||
Bruno Joliveau bruno.joliveau@savoirfairelinux.com https://github.com/bjoliveau
|
||||
David Cormier david.cormier@savoirfairelinux.com https://github.com/cormier
|
||||
David Dufresne david.dufresne@savoirfairelinux.com https://github.com/dufresnedavid
|
||||
Davin Baragiotta davin.baragiotta@savoirfairelinux.com https://github.com/giotta
|
||||
El Hadji Dem elhadji.dem@savoirfairelinux.com https://github.com/ehdem
|
||||
Guillaume Auger guillaume.auger@savoirfairelinux.com https://github.com/jehog
|
||||
Joao Alfredo Gama Batista joao.gama@savoirfairelinux.com https://github.com/joaoalf
|
||||
Jordi Riera jordi.riera@savoirfairelinux.com https://github.com/foutoucour
|
||||
Julien Roux julien.roux@savoirfairelinux.com https://github.com/jrouxsfl
|
||||
Loïc Faure-Lacroix loic.lacroix@savoirfairelinux.com https://github.com/llacroix
|
||||
Maxime Chambreuil maxime.chambreuil@savoirfairelinux.com https://github.com/max3903
|
||||
Pierre Gault pierre.gault@savoirfairelinux.com https://github.com/gaultp
|
||||
Pierre Lamarche pierre.lamarche@savoirfairelinux.com https://github.com/plamarche
|
||||
Sandy Carter sandy.carter@savoirfairelinux.com https://github.com/bwrsandman
|
||||
Vincent Vinet vincent.vinet@savoirfairelinux.com https://github.com/veloutin
|
||||
@@ -0,0 +1,16 @@
|
||||
India, U.A.E, Singapore, 2015-02-10
|
||||
|
||||
Tech Receptives, Tech-Receptives Solutions Pvt. Ltd, Tech Receptives IT Solutions FZC and Tech Receptives Asia Pacific Pte Ltd agrees to the terms of the Odoo Corporate
|
||||
Contributor License Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Parthiv Patel parthiv@techreceptives.com https://github.com/techreceptives
|
||||
|
||||
List of contributors:
|
||||
|
||||
Parthiv Patel parthiv@techreceptives.com https://github.com/parthivgls
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
México, 9-2-2015
|
||||
|
||||
Vauxoo agrees to the terms of the Odoo Corporate Contributor License Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Moisés López moylop260@vauxoo.com https://github.com/moylop260
|
||||
|
||||
List of contributors:
|
||||
|
||||
Humberto Arocha hbto@vauxoo.com https://github.com/hbto
|
||||
Javier Duran javier@vauxoo.com
|
||||
Moisés López moylop260@vauxoo.com https://github.com/moylop260
|
||||
Nhomar Hernández nhomar@vauxoo.com https://github.com/nhomar
|
||||
Sabirna Romero sabrina@vauxoo.com https://github.com/dsabrinarg
|
||||
Yanina Aular yanina.aular@vauxoo.com https://github.com/yaniaular
|
||||
@@ -0,0 +1,47 @@
|
||||
# Odoo Individual Contributor License Agreement
|
||||
## Odoo ICLA v1.0
|
||||
Based on the Apache Software Foundation Individual Contributor License Agreement v2.0, with modifications
|
||||
|
||||
Thank you for your interest in an Odoo S.A. (the "Project Leads" ) open source project. In order to clarify the intellectual property license granted with Contributions from any person or entity, the Odoo Project Leads must have a Contributor License Agreement (the "Agreement") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of the Project, its users, and the Odoo Project Leads; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete and sign the Agreement by:
|
||||
|
||||
* following the electronic procedure to complete, sign and submit the ICLA at https://www.odoo.com/sign-cla
|
||||
* or scanning and emailing the signed Agreement to cla-submission@odoo.com
|
||||
|
||||
**Please read this document carefully before signing and keep a copy for your records.**
|
||||
|
||||
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Project. Except for the license granted herein to the Project Leads and recipients of software distributed by the Project Leads, You reserve all right, title, and interest in and to Your Contributions.
|
||||
|
||||
1. Definitions.
|
||||
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to the Project Leads for inclusion in, or documentation of, any of the products managed or maintained by the Project Leads (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Project Leads or their representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Project Leads for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project Leads and to recipients of software distributed by the Project Leads a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
|
||||
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to the Project Leads and to recipients of software distributed by the Project Leads a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
|
||||
4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to the Project Leads, or that your employer has executed a separate Corporate Contributor License Agreement with the Project Leads.
|
||||
|
||||
|
||||
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
|
||||
|
||||
|
||||
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
7. Should You wish to submit work that is not Your original creation, You may submit it to the Project Leads separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
|
||||
|
||||
8. You agree to notify the Project Leads of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
|
||||
|
||||
|
||||
```
|
||||
Full Name: _________________________________________________
|
||||
Email: _____________________________________________________
|
||||
Telephone: _________________________________________________
|
||||
Mailing Address: ___________________________________________
|
||||
___________________________________________
|
||||
Country: ___________________________________________________
|
||||
Signature: _________________________________________________
|
||||
Date: ______________________________________________________
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
Costa Rica, 2015-02-08
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Mario Arias Badilla the.clone.master@gmail.com https://github.com/TheCloneMaster
|
||||
@@ -0,0 +1,11 @@
|
||||
Estonia, 2015-02-10
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Anton Chepurov anton.chepurov@gmail.com https://github.com/a0c
|
||||
@@ -0,0 +1,11 @@
|
||||
Belgium, 2015-02-07
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Adrien Peiffer adrien.peiffer@acsone.eu https://github.com/adrienpeiffer
|
||||
@@ -0,0 +1,11 @@
|
||||
Egypt, 2015-02-07
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Mohammad Alhashash alhashash@alhashash.net https://github.com/alhashash
|
||||
@@ -0,0 +1,11 @@
|
||||
China, 2015-02-09
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Buke Wang wangbuke@gmail.com https://github.com/buke
|
||||
@@ -0,0 +1,11 @@
|
||||
France, 2015-02-07
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Christophe Combelles ccomb@free.fr https://github.com/ccomb
|
||||
@@ -0,0 +1,11 @@
|
||||
R. Macedonia, 2015-02-08
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Kiril Vangelovski kiril@hacbee.com https://github.com/kirca
|
||||
@@ -0,0 +1,9 @@
|
||||
Germany, 2015-02-08
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Stefan Reisich nafex@gmx.net https://github.com/nafex
|
||||
@@ -0,0 +1,11 @@
|
||||
France, 2015-02-09,
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Nicolas Piganeau nicolas.piganeau@ndp-systemes.fr https://github.com/npiganeau
|
||||
@@ -0,0 +1,11 @@
|
||||
Belgium, 2015-02-06
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Olivier Dony odo@odoo.com https://github.com/odony
|
||||
@@ -0,0 +1,154 @@
|
||||
# Sign the Odoo CLA
|
||||
|
||||
In order for your contribution to Odoo to be accepted, you have to sign the
|
||||
Odoo Contributor License Agreement (CLA). More information about this
|
||||
requirement is explained in the [FAQ](#faq).
|
||||
|
||||
## If you are an individual
|
||||
|
||||
1. Read the [Individual Contributor License Agreement](icla-1.0.md)
|
||||
|
||||
2. Modify your current pull request, or make a new pull request on
|
||||
[odoo/odoo](/odoo/odoo), adding a new file `<github-login>.md` under the
|
||||
[`doc/cla/individual`](individual/) directory. If your GitHub login is
|
||||
`odony`, the file would be `doc/cla/individual/odony.md`. The file should
|
||||
contain:
|
||||
|
||||
```
|
||||
<country>, <date>
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
<name> <email> https://github.com/<login>
|
||||
```
|
||||
|
||||
Replacing the following placeholders:
|
||||
|
||||
* `<country>`: your country
|
||||
* `<date>`: current date in the form `YYYY-MM-DD`
|
||||
* `<name>`: your name
|
||||
* `<email>`: your email
|
||||
* `<login>`: your GitHub login
|
||||
|
||||
3. An Odoo R&D Team member will verify and accept your Pull Request. You can
|
||||
make other pull requests, but we won't be able to merge them until your CLA
|
||||
signature is merged.
|
||||
|
||||
## If you work for a company
|
||||
|
||||
1. Read the [Corporate Contributor License Agreement](icla-1.0.md)
|
||||
|
||||
2. Modify your current pull request, or make a new pull request on
|
||||
[odoo/odoo](/odoo/odoo), adding a new file `<lowercase-company-name>.md`
|
||||
under the [`doc/cla/corporate`](corporate/) directory.
|
||||
If the name of the company is Odoo, the file would be
|
||||
`doc/cla/corporate/odoo.md`. The file should contain:
|
||||
|
||||
```
|
||||
<country>, <date>
|
||||
|
||||
<company-name> agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
<name> <email> https://github.com/<login>
|
||||
|
||||
List of contributors:
|
||||
|
||||
<name> <email> https://github.com/<login>
|
||||
```
|
||||
|
||||
The List of contributors should list the individual contributors working
|
||||
for the company.
|
||||
|
||||
Replacing the following placeholders:
|
||||
|
||||
* `<country>`: your country
|
||||
* `<date>`: current date in the form `YYYY-MM-DD`
|
||||
* `<name>`: your name
|
||||
* `<email>`: your email
|
||||
* `<login>`: your GitHub login
|
||||
|
||||
3. An Odoo R&D Team member will verify and accept your Pull Request. You can
|
||||
make other pull requests, but we won't be able to merge them until your CLA
|
||||
signature is merged.
|
||||
|
||||
## If you dont have a github account
|
||||
|
||||
If you cannot submit your signature using a pull request, you may alternatively
|
||||
print the CLA, complete it, sign it, scan it and send it by email to
|
||||
`cla-submission` `at` `odoo.com`. In that case someone from the Odoo team will
|
||||
make the pull request on your behalf.
|
||||
|
||||
* Printable Odoo CCLA v1.0 https://www.odoo.com/files/legal/Odoo-CCLA-v1.0.pdf
|
||||
* Printable Odoo ICLA v1.0 https://www.odoo.com/files/legal/Odoo-ICLA-v1.0.pdf
|
||||
|
||||
# FAQ
|
||||
|
||||
## Why do i need to accept a CLA ?
|
||||
|
||||
The goal of having a Contributor License Agreement for Odoo is to:
|
||||
|
||||
* clearly define the terms under which intellectual property (patches, pull
|
||||
requests, etc.) have been contributed to the Odoo project
|
||||
|
||||
* protect the Odoo project and its users in case of legal dispute about the
|
||||
origin or ownership of any part of the code
|
||||
|
||||
* protect the Odoo project and its users from bad actors who would contribute
|
||||
and then try to withdraw their contributions or cause legal trouble, e.g. in
|
||||
the form of patent lawsuits
|
||||
|
||||
This is done by establishing a credible, non-repudiable record that each
|
||||
contributor really intended to contribute to the Odoo project under specific
|
||||
terms, and they had the right to make those contributions.
|
||||
|
||||
The CLA is for the protection of the contributors, the project and its users.
|
||||
It does not change your rights to use your own contributions for any other
|
||||
purpose.
|
||||
|
||||
The Odoo CLA is based on the Apache Software Foundation CLA v2.0, as
|
||||
can be found on the Apache website.
|
||||
|
||||
This CLA is not a copyright assignment, it is a pure license agreement. You
|
||||
keep the full copyright for your contributions, you simply provide an
|
||||
irrevocable license to the project maintainer, Odoo S.A. to use, modify and
|
||||
distribute your contributions without further restrictions.
|
||||
|
||||
## How does it work?
|
||||
|
||||
Each individual contributor (making contributions only on their own behalf) is
|
||||
required to sign and submit the Individual Contributor License Agreement
|
||||
(ICLA) of Odoo. The agreement is executed by adding your name and
|
||||
signature to the list of validated contributors inside the project source code.
|
||||
|
||||
If someone is unable to sign the CLA, their contributions will have to be
|
||||
removed from the Odoo project.
|
||||
|
||||
In addition, if some or all of someone's contributions are written as part of
|
||||
an employment by somebody else, the work may not belong to the contributor but
|
||||
to their employer, depending on the contract terms and local laws. In that case
|
||||
the employer needs to sign the Corporate Contributor License Agreement (CCLA),
|
||||
including the names of all contributors allowed to make those contributions in
|
||||
order for those contributions to be accepted.
|
||||
|
||||
The contributors should still sign the Individual CLA, in order to cover the
|
||||
contributions that do not belong to the employer.
|
||||
|
||||
Contributors who have signed the ICLA without a CCLA from their employer should
|
||||
be very careful.
|
||||
|
||||
The ICLA (section 4) is a legal declaration where the contributor states they
|
||||
have the right to make the contributions. These contributors should only
|
||||
submit contributions they are really entitled to license.
|
||||
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Runme From the repo toplevel directory
|
||||
#
|
||||
import subprocess
|
||||
import glob
|
||||
import re
|
||||
import pprint
|
||||
|
||||
|
||||
cla_glob = "doc/cla/*/*.md"
|
||||
cla = ''.join(open(f).read() for f in glob.glob(cla_glob))
|
||||
cla = cla.lower()
|
||||
|
||||
def cla_signed(email):
|
||||
if re.match('.*(odoo|openerp|tinyerp).com$',email):
|
||||
return True
|
||||
if cla.find(email) != -1:
|
||||
return True
|
||||
return False
|
||||
|
||||
def blamestat(ext='py'):
|
||||
r = {}
|
||||
ok = 0
|
||||
okl = []
|
||||
ko = 0
|
||||
kol = []
|
||||
p = subprocess.Popen("git ls-tree -r -z --name-only HEAD | grep -z '.%s$' | xargs -0 -n1 git blame --line-porcelain HEAD |grep '^author-mail ' |sort |uniq -c|sort -nr" % ext, shell=True, stdout = subprocess.PIPE)
|
||||
for i in p.stdout.read().split('\n'):
|
||||
mo = re.search('(\d+) author-mail <([^ @<]+@[^ @<]+)>',i)
|
||||
if mo:
|
||||
lines = int(mo.group(1))
|
||||
email = mo.group(2)
|
||||
if cla_signed(email):
|
||||
ok += lines
|
||||
okl.append(i)
|
||||
else:
|
||||
ko += lines
|
||||
kol.append(i)
|
||||
print '-'*60
|
||||
print 'Stats for ',ext
|
||||
print '-'*60
|
||||
print "\nCLA SIGNED %s/%s (%.0f%%)" % (ok, ok+ko, ok*100.0/(ok+ko))
|
||||
for i in okl:
|
||||
print i
|
||||
|
||||
print "\nCLA MISSING %s/%s (%.0f%%)\n" % (ko, ok+ko, ko*100.0/(ok+ko))
|
||||
for i in kol:
|
||||
print i
|
||||
print
|
||||
print
|
||||
|
||||
blamestat('md')
|
||||
blamestat('rst')
|
||||
blamestat('py')
|
||||
blamestat('js')
|
||||
blamestat('xml')
|
||||
blamestat('csv')
|
||||
@@ -498,7 +498,7 @@ Two decorators can expose a new-style method to the old API:
|
||||
empty. Its "old API" signature is ``cr, uid, *arguments, context``::
|
||||
|
||||
@api.model
|
||||
def some_method(foo):
|
||||
def some_method(self, a_value):
|
||||
pass
|
||||
# can be called as
|
||||
old_style_model.some_method(cr, uid, a_value, context=context)
|
||||
@@ -508,7 +508,7 @@ Two decorators can expose a new-style method to the old API:
|
||||
"old API" signature is ``cr, uid, ids, *arguments, context``::
|
||||
|
||||
@api.multi
|
||||
def some_method(foo):
|
||||
def some_method(self, a_value):
|
||||
pass
|
||||
# can be called as
|
||||
old_style_model.some_method(cr, uid, [id1, id2], a_value, context=context)
|
||||
@@ -525,7 +525,7 @@ return lists of ids, there is also a decorator managing this:
|
||||
|
||||
>>> @api.multi
|
||||
... @api.returns('self')
|
||||
... def some_method():
|
||||
... def some_method(self):
|
||||
... return self
|
||||
>>> new_style_model = env['a.model'].browse(1, 2, 3)
|
||||
>>> new_style_model.some_method()
|
||||
|
||||
@@ -217,10 +217,11 @@ class res_partner_bank(osv.osv):
|
||||
return {'value': result}
|
||||
|
||||
|
||||
def onchange_partner_id(self, cr, uid, id, partner_id, context=None):
|
||||
def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
|
||||
result = {}
|
||||
if partner_id:
|
||||
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
|
||||
if partner_id is not False:
|
||||
# be careful: partner_id may be a NewId
|
||||
part = self.pool['res.partner'].browse(cr, uid, [partner_id], context=context)
|
||||
result['owner_name'] = part.name
|
||||
result['street'] = part.street or False
|
||||
result['city'] = part.city or False
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
<field name="city" class="oe_inline" placeholder="City"/>
|
||||
</div>
|
||||
<field name="state_id" placeholder="State" options='{"no_open": True}'/>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": True}'/>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": True, "no_create": True}'/>
|
||||
</div>
|
||||
</group>
|
||||
<group name="bank" string="Information About the Bank">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user