[MERGE] forward port branch 10.0 up to a85790be5c

This commit is contained in:
Christophe Simonis
2017-06-06 18:49:36 +02:00
18 changed files with 99 additions and 63 deletions
+1 -1
View File
@@ -981,7 +981,7 @@ class AccountMoveLine(models.Model):
second_line_dict['amount_currency'] = -second_line_dict['amount_currency']
# Create the move
writeoff_move = self.env['account.move'].create({
writeoff_move = self.env['account.move'].with_context(apply_taxes=True).create({
'journal_id': vals['journal_id'],
'date': vals['date'],
'state': 'draft',
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
import urllib
import urllib2
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
@@ -9,10 +9,10 @@ from odoo.exceptions import UserError
def geo_find(addr):
url = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address='
url += urllib.quote(addr.encode('utf8'))
url += urllib2.quote(addr.encode('utf8'))
try:
result = json.load(urllib.urlopen(url))
result = json.load(urllib2.urlopen(url))
except Exception as e:
raise UserError(_('Cannot contact geolocation servers. Please make sure that your Internet connection is up and running (%s).') % e)
+1 -1
View File
@@ -10,7 +10,7 @@ class Partner(models.Model):
team_id = fields.Many2one('crm.team', string='Sales Channel', oldname='section_id')
opportunity_ids = fields.One2many('crm.lead', 'partner_id', string='Opportunities', domain=[('type', '=', 'opportunity')])
meeting_ids = fields.Many2many('calendar.event', 'calendar_event_res_partner_rel', 'res_partner_id', 'calendar_event_id', string='Meetings')
meeting_ids = fields.Many2many('calendar.event', 'calendar_event_res_partner_rel', 'res_partner_id', 'calendar_event_id', string='Meetings', copy=False)
opportunity_count = fields.Integer("Opportunity", compute='_compute_opportunity_count')
meeting_count = fields.Integer("# Meetings", compute='_compute_meeting_count')
activities_count = fields.Integer("Activities", compute='_compute_activities_count')
@@ -513,7 +513,7 @@ class GoogleCalendar(models.AbstractModel):
if type == "write":
res = CalendarEvent.browse(event['id']).write(result)
elif type == "copy":
result['recurrence'] = True
result['recurrency'] = True
res = CalendarEvent.browse([event['id']]).write(result)
elif type == "create":
res = CalendarEvent.create(result).id
+13 -4
View File
@@ -55,9 +55,7 @@ index_template = """
<p>
If you need to grant remote debugging access to a developer, you can do it <a href='/remote_connect'>here</a>.
</p>
<p>
If you need to display the current customer basket on another device, you can do it <a href='/point_of_sale/display'>here</a>.
</p>
%s
<p>
The PosBox software installed on this posbox is <b>version 16</b>,
the posbox version number is independent from Odoo. You can upgrade
@@ -72,10 +70,21 @@ index_template = """
class PosboxHomepage(odoo.addons.web.controllers.main.Home):
def get_hw_screen_message(self):
return """
<p>
The activate the customer display feature, you will need to reinstall the PosBox software.
You can find the latest images on the <a href="http://nightly.odoo.com/master/posbox/">Odoo Nightly builds</a> website.
Make sure to download at least the version 16.<br/>
Odoo version 11, or above, is required to use the customer display feature.
</p>
"""
@http.route('/', type='http', auth='none', website=True)
def index(self):
#return request.render('hw_posbox_homepage.index',mimetype='text/html')
return index_template
return index_template % self.get_hw_screen_message()
@http.route('/wifi', type='http', auth='none', website=True)
def wifi(self):
+4 -1
View File
@@ -119,7 +119,10 @@ class Scanner(Thread):
def get_devices(self):
try:
if not evdev:
return None
return []
if not os.path.isdir(self.input_dir):
return []
new_devices = [device for device in listdir(self.input_dir)
if join(self.input_dir, device) not in [dev.evdev.fn for dev in self.open_devices]]
+9
View File
@@ -4,6 +4,7 @@
from odoo import http
from odoo.tools import config
from odoo.addons.web.controllers import main as web
from openerp.addons.hw_posbox_homepage.controllers import main as homepage
import logging
import netifaces as ni
@@ -16,6 +17,14 @@ self_port = str(config['xmlrpc_port'] or 8069)
_logger = logging.getLogger(__name__)
class Homepage(homepage.PosboxHomepage):
def get_hw_screen_message(self):
return """
<p>
If you need to display the current customer basket on another device, you can do it <a href='/point_of_sale/display'>here</a>.
</p>
"""
class HardwareScreen(web.Home):
+6 -2
View File
@@ -327,8 +327,12 @@ class MrpProduction(models.Model):
return self.env['stock.move']
if bom_line.product_id.type not in ['product', 'consu']:
return self.env['stock.move']
if self.bom_id.routing_id and self.bom_id.routing_id.location_id:
source_location = self.bom_id.routing_id.location_id
if self.routing_id:
routing = self.routing_id
else:
routing = self.bom_id.routing_id
if routing and routing.location_id:
source_location = routing.location_id
else:
source_location = self.location_src_id
original_quantity = self.product_qty - self.qty_produced
+1 -1
View File
@@ -631,7 +631,7 @@ td {
margin-right: 0;
text-align: center;
vertical-align: top;
width: 188px;
width: 186px;
border: none;
border-radius: 0;
border-top: 1px solid;
+1 -1
View File
@@ -335,7 +335,7 @@ class ResCountryGroup(models.Model):
class PricelistItem(models.Model):
_name = "product.pricelist.item"
_description = "Pricelist item"
_order = "applied_on, min_quantity desc, categ_id desc"
_order = "applied_on, min_quantity desc, categ_id desc, id"
product_tmpl_id = fields.Many2one(
'product.template', 'Product Template', ondelete='cascade',
@@ -104,7 +104,7 @@ class ProductProduct(models.Model):
#Cost price is calculated afterwards as it is a property
sqlstr = """
select
sum(l.price_unit * l.quantity)/sum(nullif(l.quantity,0)) as avg_unit_price,
sum(l.price_unit * l.quantity)/nullif(sum(l.quantity),0) as avg_unit_price,
sum(l.quantity) as num_qty,
sum(l.quantity * (l.price_subtotal/(nullif(l.quantity,0)))) as total,
sum(l.quantity * pt.list_price) as sale_expected
+4 -1
View File
@@ -253,7 +253,10 @@ class SaleOrder(models.Model):
@api.model
def create(self, vals):
if vals.get('name', _('New')) == _('New'):
vals['name'] = self.env['ir.sequence'].next_by_code('sale.order') or _('New')
if 'company_id' in vals:
vals['name'] = self.env['ir.sequence'].with_context(force_company=vals['company_id']).next_by_code('sale.order') or _('New')
else:
vals['name'] = self.env['ir.sequence'].next_by_code('sale.order') or _('New')
# Makes sure partner_invoice_id', 'partner_shipping_id' and 'pricelist_id' are defined
if any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id']):
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import unittest
from odoo.addons.stock_landed_costs.tests.common import TestStockLandedCostsCommon
class TestLandedCosts(TestStockLandedCostsCommon):
@@ -45,6 +45,10 @@ class TestLandedCosts(TestStockLandedCostsCommon):
'location_dest_id': self.customer_location_id})
def test_00_landed_costs_on_incoming_shipment(self):
chart_of_accounts = self.env.user.company_id.chart_template_id
generic_coa = self.env.ref('l10n_generic_coa.configurable_chart_template')
if chart_of_accounts != generic_coa:
raise unittest.SkipTest('Skip this test as it works only with %s (%s loaded)' % (generic_coa.name, chart_of_accounts.name))
""" Test landed cost on incoming shipment """
#
# (A) Purchase product
@@ -94,6 +98,11 @@ class TestLandedCosts(TestStockLandedCostsCommon):
self.assertEqual(account_entry['debit'], 430.0, 'Wrong Account Entry')
def test_01_negative_landed_costs_on_incoming_shipment(self):
chart_of_accounts = self.env.user.company_id.chart_template_id
generic_coa = self.env.ref('l10n_generic_coa.configurable_chart_template')
if chart_of_accounts != generic_coa:
raise unittest.SkipTest('Skip this test as it works only with %s (%s loaded)' % (generic_coa.name, chart_of_accounts.name))
""" Test negative landed cost on incoming shipment """
#
# (A) Purchase Product
+6 -6
View File
@@ -662,7 +662,7 @@ class Database(http.Controller):
request.session.authenticate(name, post['login'], password)
return http.local_redirect('/web/')
except Exception, e:
error = "Database creation error: %s" % e
error = "Database creation error: %s" % str(e) or repr(e)
return self._render_template(error=error)
@http.route('/web/database/duplicate', type='http', auth="none", methods=['POST'], csrf=False)
@@ -673,7 +673,7 @@ class Database(http.Controller):
dispatch_rpc('db', 'duplicate_database', [master_pwd, name, new_name])
return http.local_redirect('/web/database/manager')
except Exception, e:
error = "Database duplication error: %s" % e
error = "Database duplication error: %s" % str(e) or repr(e)
return self._render_template(error=error)
@http.route('/web/database/drop', type='http', auth="none", methods=['POST'], csrf=False)
@@ -683,7 +683,7 @@ class Database(http.Controller):
request._cr = None # dropping a database leads to an unusable cursor
return http.local_redirect('/web/database/manager')
except Exception, e:
error = "Database deletion error: %s" % e
error = "Database deletion error: %s" % str(e) or repr(e)
return self._render_template(error=error)
@http.route('/web/database/backup', type='http', auth="none", methods=['POST'], csrf=False)
@@ -701,7 +701,7 @@ class Database(http.Controller):
return response
except Exception, e:
_logger.exception('Database.backup')
error = "Database backup error: %s" % e
error = "Database backup error: %s" % str(e) or repr(e)
return self._render_template(error=error)
@http.route('/web/database/restore', type='http', auth="none", methods=['POST'], csrf=False)
@@ -711,7 +711,7 @@ class Database(http.Controller):
dispatch_rpc('db', 'restore', [master_pwd, name, data, str2bool(copy)])
return http.local_redirect('/web/database/manager')
except Exception, e:
error = "Database restore error: %s" % e
error = "Database restore error: %s" % str(e) or repr(e)
return self._render_template(error=error)
@http.route('/web/database/change_password', type='http', auth="none", methods=['POST'], csrf=False)
@@ -720,7 +720,7 @@ class Database(http.Controller):
dispatch_rpc('db', 'change_admin_password', [master_pwd, master_pwd_new])
return http.local_redirect('/web/database/manager')
except Exception, e:
error = "Master password update error: %s" % e
error = "Master password update error: %s" % str(e) or repr(e)
return self._render_template(error=error)
@http.route('/web/database/list', type='json', auth='none')
+20 -24
View File
@@ -61,28 +61,24 @@ class PaymentTransaction(models.Model):
def _confirm_so(self, acquirer_name=False):
for tx in self:
try:
# check tx state, confirm the potential SO
if tx.sale_order_id and tx.sale_order_id.state in ['draft', 'sent']:
# verify SO/TX match, excluding tx.fees which are currently not included in SO
amount_matches = float_compare(tx.amount, tx.sale_order_id.amount_total, 2) == 0
if amount_matches:
if not acquirer_name:
acquirer_name = tx.sale_order_id.payment_acquirer_id.provider or 'unknown'
if tx.state == 'authorized' and tx.acquirer_id.auto_confirm == 'authorize':
_logger.info('<%s> transaction authorized, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
tx.sale_order_id.with_context(send_email=True).action_confirm()
if tx.state == 'done' and tx.acquirer_id.auto_confirm in ['confirm_so', 'generate_and_pay_invoice']:
_logger.info('<%s> transaction completed, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
tx.sale_order_id.with_context(send_email=True).action_confirm()
# check tx state, confirm the potential SO
if tx.sale_order_id and tx.sale_order_id.state in ['draft', 'sent']:
# verify SO/TX match, excluding tx.fees which are currently not included in SO
amount_matches = float_compare(tx.amount, tx.sale_order_id.amount_total, 2) == 0
if amount_matches:
if not acquirer_name:
acquirer_name = tx.sale_order_id.payment_acquirer_id.provider or 'unknown'
if tx.state == 'authorized' and tx.acquirer_id.auto_confirm == 'authorize':
_logger.info('<%s> transaction authorized, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
tx.sale_order_id.with_context(send_email=True).action_confirm()
if tx.state == 'done' and tx.acquirer_id.auto_confirm in ['confirm_so', 'generate_and_pay_invoice']:
_logger.info('<%s> transaction completed, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
tx.sale_order_id.with_context(send_email=True).action_confirm()
if tx.acquirer_id.auto_confirm == 'generate_and_pay_invoice':
self._generate_and_pay_invoice(tx, acquirer_name)
elif tx.state not in ['cancel', 'error'] and tx.sale_order_id.state == 'draft':
_logger.info('<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
tx.sale_order_id.force_quotation_send()
else:
_logger.warning('<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
except Exception:
_logger.exception('Fail to confirm the order or send the confirmation email%s', tx and ' for the transaction %s' % tx.reference or '')
if tx.acquirer_id.auto_confirm == 'generate_and_pay_invoice':
self._generate_and_pay_invoice(tx, acquirer_name)
elif tx.state not in ['cancel', 'error'] and tx.sale_order_id.state == 'draft':
_logger.info('<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
tx.sale_order_id.force_quotation_send()
else:
_logger.warning('<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
+16 -13
View File
@@ -1,4 +1,4 @@
Canada, France, 8-2-2015
Canada, France, 2017-05-12
Savoir-faire Linux agrees to the terms of the Odoo Corporate Contributor License
Agreement v1.0.
@@ -8,22 +8,25 @@ declaration.
Signed,
Maxime Chambreuil partners@savoirfairelinux.com https://github.com/savoirfairelinux
Jérome Boisvert-Chouinard jerome.boisvertchouinard@savoirfairelinux.com https://github.com/jbchouinard
List of contributors:
Agathe Mollé agathe.molle@savoirfairelinux.com https://github.com/Ehtaga
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
Jérome Boisvert-Chouinard jerome.boisvertchouinard@savoirfairelinux.com https://github.com/jbchouinard
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
Julie Moussu julie.mousse@savoirfairelinux.com https://github.com/JulieSFL
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
David Cormier david.cormier@savoirfairelinux.com https://github.com/cormier (up to 2017-05-12)
Davin Baragiotta davin.baragiotta@savoirfairelinux.com https://github.com/giotta (up to 2017-05-12)
El Hadji Dem elhadji.dem@savoirfairelinux.com https://github.com/ehdem (up to 2017-05-12)
Guillaume Auger guillaume.auger@savoirfairelinux.com https://github.com/jehog (up to 2017-05-12)
Jordi Riera jordi.riera@savoirfairelinux.com https://github.com/foutoucour (up to 2017-05-12)
Julien Roux julien.roux@savoirfairelinux.com https://github.com/jrouxsfl (up to 2017-05-12)
Loïc Faure-Lacroix loic.lacroix@savoirfairelinux.com https://github.com/llacroix (up to 2017-05-12)
Maxime Chambreuil maxime.chambreuil@savoirfairelinux.com https://github.com/max3903 (up to 2017-05-12)
Pierre Gault pierre.gault@savoirfairelinux.com https://github.com/gaultp (up to 2017-05-12)
Sandy Carter sandy.carter@savoirfairelinux.com https://github.com/bwrsandman (up to 2017-05-12)
Vincent Vinet vincent.vinet@savoirfairelinux.com https://github.com/veloutin (up to 2017-05-12)
+1 -1
View File
@@ -9,4 +9,4 @@ declaration.
Signed,
Jesús Ventosinos Mayor jesus@pexego.es https://github.com/jesusVMayor
Jesús Ventosinos Mayor jesus@comunitea.com https://github.com/jesusVMayor
+1 -1
View File
@@ -678,7 +678,7 @@ class BaseModel(object):
field = cls._fields.get(name)
if not field:
_logger.warning("method %s.%s: @constrains parameter %r is not a field name", cls._name, attr, name)
elif not (field.store or field.inverse):
elif not (field.store or field.inverse or field.inherited):
_logger.warning("method %s.%s: @constrains parameter %r is not writeable", cls._name, attr, name)
methods.append(func)