diff --git a/addons/account/account.py b/addons/account/account.py index d74bfce2f08..d4c34f49922 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1154,6 +1154,19 @@ class account_move(osv.osv): _description = "Account Entry" _order = 'id desc' + def account_assert_balanced(self, cr, uid, context=None): + cr.execute("""\ + SELECT move_id + FROM account_move_line + WHERE state = 'valid' + GROUP BY move_id + HAVING abs(sum(debit) - sum(credit)) > 0.00001 + """) + assert len(cr.fetchall()) == 0, \ + "For all Journal Items, the state is valid implies that the sum " \ + "of credits equals the sum of debits" + return True + def account_move_prepare(self, cr, uid, journal_id, date=False, ref='', company_id=False, context=None): ''' Prepares and returns a dictionary of values, ready to be passed to create() based on the parameters received. @@ -2023,7 +2036,7 @@ class account_tax(osv.osv): data['tax_amount']=quantity # data['amount'] = quantity elif tax.type=='code': - localdict = {'price_unit':cur_price_unit, 'product':product, 'partner':partner} + localdict = {'price_unit':cur_price_unit, 'product':product, 'partner':partner, 'quantity': quantity} exec tax.python_compute in localdict amount = localdict['result'] data['amount'] = amount diff --git a/addons/account/account_assert_test.xml b/addons/account/account_assert_test.xml index 88025514763..32e8afb1b50 100644 --- a/addons/account/account_assert_test.xml +++ b/addons/account/account_assert_test.xml @@ -1,8 +1,6 @@ - - - + diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 22bce5306be..9724451df5f 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -125,6 +125,9 @@ class account_voucher(osv.osv): journal_pool = self.pool.get('account.journal') journal_id = context.get('journal_id', False) if journal_id: + if isinstance(journal_id, (list, tuple)): + # sometimes journal_id is a pair (id, display_name) + journal_id = journal_id[0] journal = journal_pool.browse(cr, uid, journal_id, context=context) if journal.currency: return journal.currency.id diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 2c06865df44..e259de4cb44 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -789,7 +789,7 @@ class crm_lead(format_address, osv.osv): partner_id = self._create_lead_partner(cr, uid, lead, context) self.pool['res.partner'].write(cr, uid, partner_id, {'team_id': lead.team_id and lead.team_id.id or False}) if partner_id: - lead.write({'partner_id': partner_id}, context=context) + lead.write({'partner_id': partner_id}) partner_ids[lead.id] = partner_id return partner_ids diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index 4ef3224fc3e..a7e8b5d3716 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -53,10 +53,6 @@ class sale_order(osv.Model): result['value']['carrier_id'] = dtype return result - def _prepare_order_picking(self, cr, uid, order, context=None): - result = super(sale_order, self)._prepare_order_picking(cr, uid, order, context=context) - result.update(carrier_id=order.carrier_id.id) - return result def _delivery_unset(self, cr, uid, ids, context=None): sale_obj = self.pool['sale.order.line'] @@ -92,4 +88,4 @@ class sale_order(osv.Model): 'price_unit': grid_obj.get_price(cr, uid, grid.id, order, time.strftime('%Y-%m-%d'), context), 'tax_id': [(6, 0, taxes_ids)], 'is_delivery': True - }) + }) \ No newline at end of file diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py index 7bfd52df420..c8781244fe0 100644 --- a/addons/delivery/stock.py +++ b/addons/delivery/stock.py @@ -120,9 +120,10 @@ class stock_picking(osv.osv): } def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None): + invoice_obj = self.pool.get('account.invoice') invoice_line_obj = self.pool.get('account.invoice.line') invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context) - invoice = self.browse(cr, uid, invoice_id, context=context) + invoice = invoice_obj.browse(cr, uid, invoice_id, context=context) invoice_line = self._prepare_shipping_invoice_line(cr, uid, picking, invoice, context=context) if invoice_line: invoice_line_obj.create(cr, uid, invoice_line) @@ -170,6 +171,24 @@ class stock_move(osv.osv): 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of Measure (Unit of Measure) is the unit of measurement for Weight",), } + def action_confirm(self, cr, uid, ids, context=None): + """ + Pass the carrier to the picking from the sales order + (Should also work in case of Phantom BoMs when on explosion the original move is deleted) + """ + procs_to_check = [] + for move in self.browse(cr, uid, ids, context=context): + if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.carrier_id: + procs_to_check += [move.procurement_id] + res = super(stock_move, self).action_confirm(cr, uid, ids, context=context) + pick_obj = self.pool.get("stock.picking") + for proc in procs_to_check: + pickings = list(set([x.picking_id.id for x in proc.move_ids if x.picking_id and not x.picking_id.carrier_id])) + if pickings: + pick_obj.write(cr, uid, pickings, {'carrier_id': proc.sale_line_id.order_id.carrier_id.id}, context=context) + return res + + def _get_default_uom(self, cr, uid, context=None): uom_categ_id = self.pool.get('ir.model.data').xmlid_to_res_id(cr, uid, 'product.product_uom_categ_kgm') return self.pool.get('product.uom').search(cr, uid, [('category_id', '=', uom_categ_id),('factor','=',1)])[0] diff --git a/addons/fleet/fleet.py b/addons/fleet/fleet.py index 6f62d114c0d..1b2b96ffbfb 100644 --- a/addons/fleet/fleet.py +++ b/addons/fleet/fleet.py @@ -426,8 +426,10 @@ class fleet_vehicle_odometer(osv.Model): res = {} for record in self.browse(cr, uid, ids, context=context): name = record.vehicle_id.name - if record.date: - name = name+ ' / '+ str(record.date) + if not name: + name = record.date + elif record.date: + name += ' / '+ record.date res[record.id] = name return res diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py index 03a311dd2ff..3d1a87661d2 100644 --- a/addons/google_calendar/google_calendar.py +++ b/addons/google_calendar/google_calendar.py @@ -494,7 +494,7 @@ class google_calendar(osv.AbstractModel): all_my_attendees = self.pool['calendar.attendee'].search(cr, uid, [('partner_id', '=', current_user.partner_id.id)], context=context) self.pool['calendar.attendee'].write(cr, uid, all_my_attendees, {'oe_synchro_date': False, 'google_internal_event_id': False}, context=context) - current_user.write(reset_data, context=context) + current_user.write(reset_data) return True def synchronize_events_cron(self, cr, uid, context=None): @@ -552,7 +552,7 @@ class google_calendar(osv.AbstractModel): lastSync = False _logger.info("[%s] Calendar Synchro - MODE FULL SYNCHRO FORCED" % user_to_sync) else: - current_user.write({'google_calendar_cal_id': current_google}, context=context) + current_user.write({'google_calendar_cal_id': current_google}) lastSync = False _logger.info("[%s] Calendar Synchro - MODE FULL SYNCHRO - NEW CAL ID" % user_to_sync) @@ -562,7 +562,7 @@ class google_calendar(osv.AbstractModel): res = self.update_events(cr, uid, lastSync, context) - current_user.write({'google_calendar_last_sync_date': ask_time}, context=context) + current_user.write({'google_calendar_last_sync_date': ask_time}) return { "status": res and "need_refresh" or "no_new_event_form_google", "url": '' diff --git a/addons/hr/static/src/xml/suggestions.xml b/addons/hr/static/src/xml/suggestions.xml index 816011852ca..6b7fbd43bf7 100644 --- a/addons/hr/static/src/xml/suggestions.xml +++ b/addons/hr/static/src/xml/suggestions.xml @@ -24,7 +24,7 @@
- X + X
diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 071441a2c81..833da7ca684 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -493,7 +493,7 @@ class hr_applicant(osv.Model): address_id = self.pool.get('res.partner').address_get(cr, uid, [applicant.partner_id.id], ['contact'])['contact'] contact_name = self.pool.get('res.partner').name_get(cr, uid, [applicant.partner_id.id])[0][1] if applicant.job_id and (applicant.partner_name or contact_name): - applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1}, context=context) + applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1}) create_ctx = dict(context, mail_broadcast=True) emp_id = hr_employee.create(cr, uid, {'name': applicant.partner_name or contact_name, 'job_id': applicant.job_id.id, diff --git a/addons/hr_timesheet/security/ir.model.access.csv b/addons/hr_timesheet/security/ir.model.access.csv index b48988700db..757cd84f4b5 100644 --- a/addons/hr_timesheet/security/ir.model.access.csv +++ b/addons/hr_timesheet/security/ir.model.access.csv @@ -10,3 +10,4 @@ access_product_uom_hr_timesheet,product.uom.hr.timesheet,product.model_product_u access_account_fiscalyear_hr_user,account.account.fiscalyear.user,account.model_account_fiscalyear,base.group_hr_user,1,1,1,1 access_hr_analytic_timesheet_user,hr.analytic.timesheet.user,model_hr_analytic_timesheet,base.group_user,1,1,1,0 access_account_analytic_journal_user,account.account.analytic.journal.user,analytic.model_account_analytic_journal,base.group_user,1,0,0,0 +access_hr_timesheet_report,hr.timesheet.report,model_hr_timesheet_report,base.group_hr_manager,1,1,0,0 diff --git a/addons/hr_timesheet_sheet/security/ir.model.access.csv b/addons/hr_timesheet_sheet/security/ir.model.access.csv index 8eef30b35c4..2ad3c7d5ca2 100644 --- a/addons/hr_timesheet_sheet/security/ir.model.access.csv +++ b/addons/hr_timesheet_sheet/security/ir.model.access.csv @@ -3,6 +3,5 @@ access_hr_timesheet_sheet_sheet_user,hr_timesheet_sheet.sheet.user,model_hr_time access_hr_timesheet_sheet_sheet_system_employee,hr_timesheet_sheet.sheet.system.employee,model_hr_timesheet_sheet_sheet,base.group_user,1,1,1,0 access_hr_timesheet_sheet_sheet_day,hr_timesheet_sheet.sheet.day,model_hr_timesheet_sheet_sheet_day,base.group_hr_user,1,1,1,1 access_hr_timesheet_sheet_sheet_account,hr_timesheet_sheet.sheet.account,model_hr_timesheet_sheet_sheet_account,base.group_hr_user,1,1,1,1 -access_hr_timesheet_report,hr.timesheet.report,model_hr_timesheet_report,base.group_hr_manager,1,1,0,0 access_hr_analytic_timesheet_system_user,hr.analytic.timesheet.system.user,model_hr_analytic_timesheet,base.group_user,1,0,0,0 access_hr_timesheet_sheet_sheet_day,hr.timesheet.sheet.sheet.day.user,model_hr_timesheet_sheet_sheet_day,base.group_user,1,1,1,0 diff --git a/addons/mail/static/src/xml/suggestions.xml b/addons/mail/static/src/xml/suggestions.xml index 8279726ca45..52bc0aea4eb 100644 --- a/addons/mail/static/src/xml/suggestions.xml +++ b/addons/mail/static/src/xml/suggestions.xml @@ -25,7 +25,7 @@
- X + X
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 8b3da7b49c1..914cfc376a6 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -334,6 +334,11 @@ class mrp_bom(osv.osv): res['value'].update({'product_uom': product.uom_id.id}) return res + def unlink(self, cr, uid, ids, context=None): + if self.pool['mrp.production'].search(cr, uid, [('bom_id', 'in', ids), ('state', 'not in', ['done', 'cancel'])], context=context): + raise osv.except_osv(_('Warning!'), _('You can not delete a Bill of Material with running manufacturing orders.\nPlease close or cancel it first.')) + return super(mrp_bom, self).unlink(cr, uid, ids, context=context) + def onchange_product_tmpl_id(self, cr, uid, ids, product_tmpl_id, product_qty=0, context=None): """ Changes UoM and name if product_id changes. @param product_id: Changed product_id @@ -1200,7 +1205,7 @@ class mrp_production(osv.osv): self._make_service_procurement(cr, uid, line, context=context) if stock_moves: self.pool.get('stock.move').action_confirm(cr, uid, stock_moves, context=context) - production.write({'state': 'confirmed'}, context=context) + production.write({'state': 'confirmed'}) return 0 def action_assign(self, cr, uid, ids, context=None): diff --git a/addons/point_of_sale/security/point_of_sale_security.xml b/addons/point_of_sale/security/point_of_sale_security.xml index 58096a0b21d..1a472061220 100644 --- a/addons/point_of_sale/security/point_of_sale_security.xml +++ b/addons/point_of_sale/security/point_of_sale_security.xml @@ -25,5 +25,11 @@ [('company_id','child_of',[user.company_id.id])] + + Point Of Sale Order Analysis multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index 2f647bbcb15..4e630488c96 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -1169,6 +1169,9 @@ td { } @media print { + * { + color: black !important; + } body { margin: 0; } diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index c746d12c8fc..07be5f01bb2 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -811,10 +811,13 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal get_unit_price: function(){ return this.price; }, - get_display_price: function(){ + get_base_price: function(){ var rounding = this.pos.currency.rounding; return round_pr(round_pr(this.get_unit_price() * this.get_quantity(),rounding) * (1- this.get_discount()/100.0),rounding); }, + get_display_price: function(){ + return this.get_base_price(); + }, get_price_without_tax: function(){ return this.get_all_prices().priceWithoutTax; }, @@ -830,7 +833,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal get_all_prices: function(){ var self = this; var currency_rounding = this.pos.currency.rounding; - var base = round_pr(this.get_quantity() * this.get_unit_price() * (1.0 - (this.get_discount() / 100.0)), currency_rounding); + var base = this.get_base_price(); var totalTax = base; var totalNoTax = base; diff --git a/addons/portal/__openerp__.py b/addons/portal/__openerp__.py index 361971b0d92..042f2f20381 100644 --- a/addons/portal/__openerp__.py +++ b/addons/portal/__openerp__.py @@ -33,7 +33,7 @@ Customize access to your OpenERP database to external users by creating portals. ================================================================================ A portal defines a specific user menu and access rights for its members. This -menu can ben seen by portal members, public users and any other user that +menu can be seen by portal members, public users and any other user that have the access to technical features (e.g. the administrator). Also, each portal member is linked to a specific partner. diff --git a/addons/product/product.py b/addons/product/product.py index 3bcdf41cc2b..128aae5b1f7 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -847,7 +847,7 @@ class product_product(osv.osv): context['uom'], value, uom.id) value = value - product.price_extra - return product.write({'list_price': value}, context=context) + return product.write({'list_price': value}) def _get_partner_code_name(self, cr, uid, ids, product, partner_id, context=None): for supinfo in product.seller_ids: @@ -898,8 +898,8 @@ class product_product(osv.osv): res = self.write(cr, uid, [id], {'image_variant': image}, context=context) product = self.browse(cr, uid, id, context=context) if not product.product_tmpl_id.image: - product.write({'image_variant': None}, context=context) - product.product_tmpl_id.write({'image': image}, context=context) + product.write({'image_variant': None}) + product.product_tmpl_id.write({'image': image}) return res def _get_price_extra(self, cr, uid, ids, name, args, context=None): @@ -1062,7 +1062,7 @@ class product_product(osv.osv): if not limit or len(ids) < limit: # we may underrun the limit because of dupes in the results, that's fine limit2 = (limit - len(ids)) if limit else False - ids.update(self.search(cr, user, args + [('name', operator, name)], limit=limit2, context=context)) + ids.update(self.search(cr, user, args + [('name', operator, name), ('id', 'not in', list(ids))], limit=limit2, context=context)) ids = list(ids) elif not ids and operator in expression.NEGATIVE_TERM_OPERATORS: ids = self.search(cr, user, args + ['&', ('default_code', operator, name), ('name', operator, name)], limit=limit, context=context) diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index 39f48c47ea9..36e57341b2d 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -103,50 +103,57 @@ class project_work(osv.osv): res['product_uom_id'] = emp.product_id.uom_id.id return res - def create(self, cr, uid, vals, *args, **kwargs): - timesheet_obj = self.pool.get('hr.analytic.timesheet') - task_obj = self.pool.get('project.task') - uom_obj = self.pool.get('product.uom') + def _create_analytic_entries(self, cr, uid, vals, context): + """Create the hr analytic timesheet from project task work""" + timesheet_obj = self.pool['hr.analytic.timesheet'] + task_obj = self.pool['project.task'] vals_line = {} + timeline_id = False + acc_id = False + + task_obj = task_obj.browse(cr, uid, vals['task_id'], context=context) + result = self.get_user_related_details(cr, uid, vals.get('user_id', uid)) + vals_line['name'] = '%s: %s' % (tools.ustr(task_obj.name), tools.ustr(vals['name'] or '/')) + vals_line['user_id'] = vals['user_id'] + vals_line['product_id'] = result['product_id'] + if vals.get('date'): + vals_line['date' ] = vals['date'][:10] + + # Calculate quantity based on employee's product's uom + vals_line['unit_amount'] = vals['hours'] + + default_uom = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.project_time_mode_id.id + if result['product_uom_id'] != default_uom: + vals_line['unit_amount'] = self.pool['product.uom']._compute_qty(cr, uid, default_uom, vals['hours'], result['product_uom_id']) + acc_id = task_obj.project_id and task_obj.project_id.analytic_account_id.id or acc_id + if acc_id: + vals_line['account_id'] = acc_id + res = timesheet_obj.on_change_account_id(cr, uid, False, acc_id) + if res.get('value'): + vals_line.update(res['value']) + vals_line['general_account_id'] = result['general_account_id'] + vals_line['journal_id'] = result['journal_id'] + vals_line['amount'] = 0.0 + vals_line['product_uom_id'] = result['product_uom_id'] + amount = vals_line['unit_amount'] + prod_id = vals_line['product_id'] + unit = False + timeline_id = timesheet_obj.create(cr, uid, vals=vals_line, context=context) + + # Compute based on pricetype + amount_unit = timesheet_obj.on_change_unit_amount(cr, uid, timeline_id, + prod_id, amount, False, unit, vals_line['journal_id'], context=context) + if amount_unit and 'amount' in amount_unit.get('value',{}): + updv = { 'amount': amount_unit['value']['amount'] } + timesheet_obj.write(cr, uid, [timeline_id], updv, context=context) + + return timeline_id + + def create(self, cr, uid, vals, *args, **kwargs): context = kwargs.get('context', {}) if not context.get('no_analytic_entry',False): - task_obj = task_obj.browse(cr, uid, vals['task_id']) - result = self.get_user_related_details(cr, uid, vals.get('user_id', uid)) - vals_line['name'] = '%s: %s' % (tools.ustr(task_obj.name), tools.ustr(vals['name'] or '/')) - vals_line['user_id'] = vals['user_id'] - vals_line['product_id'] = result['product_id'] - vals_line['date'] = vals['date'][:10] - - # Calculate quantity based on employee's product's uom - vals_line['unit_amount'] = vals['hours'] - - default_uom = self.pool.get('res.users').browse(cr, uid, uid).company_id.project_time_mode_id.id - if result['product_uom_id'] != default_uom: - vals_line['unit_amount'] = uom_obj._compute_qty(cr, uid, default_uom, vals['hours'], result['product_uom_id']) - acc_id = task_obj.project_id and task_obj.project_id.analytic_account_id.id or False - if acc_id: - vals_line['account_id'] = acc_id - res = timesheet_obj.on_change_account_id(cr, uid, False, acc_id) - if res.get('value'): - vals_line.update(res['value']) - vals_line['general_account_id'] = result['general_account_id'] - vals_line['journal_id'] = result['journal_id'] - vals_line['amount'] = 0.0 - vals_line['product_uom_id'] = result['product_uom_id'] - amount = vals_line['unit_amount'] - prod_id = vals_line['product_id'] - unit = False - context = dict(context, recompute=True) - timeline_id = timesheet_obj.create(cr, uid, vals_line, context=context) - - # Compute based on pricetype - amount_unit = timesheet_obj.on_change_unit_amount(cr, uid, timeline_id, - prod_id, amount, False, unit, vals_line['journal_id'], context=context) - if amount_unit and 'amount' in amount_unit.get('value',{}): - updv = { 'amount': amount_unit['value']['amount'] } - timesheet_obj.write(cr, uid, [timeline_id], updv, context=context) - vals['hr_analytic_timesheet_id'] = timeline_id + vals['hr_analytic_timesheet_id'] = self._create_analytic_entries(cr, uid, vals, context=context) return super(project_work,self).create(cr, uid, vals, *args, **kwargs) def write(self, cr, uid, ids, vals, context=None): @@ -234,6 +241,10 @@ class task(osv.osv): def write(self, cr, uid, ids, vals, context=None): if context is None: context = {} + task_work_obj = self.pool['project.task.work'] + acc_id = False + missing_analytic_entries = {} + if vals.get('project_id',False) or vals.get('name',False): vals_line = {} hr_anlytic_timesheet = self.pool.get('hr.analytic.timesheet') @@ -245,6 +256,16 @@ class task(osv.osv): if len(task_obj.work_ids): for task_work in task_obj.work_ids: if not task_work.hr_analytic_timesheet_id: + if acc_id : + # missing timesheet activities to generate + missing_analytic_entries[task_work.id] = { + 'name' : task_work.name, + 'user_id' : task_work.user_id.id, + 'date' : task_work.date and task_work.date[:10] or False, + 'account_id': acc_id, + 'hours' : task_work.hours, + 'task_id' : task_obj.id + } continue line_id = task_work.hr_analytic_timesheet_id.id if vals.get('project_id',False): @@ -252,7 +273,14 @@ class task(osv.osv): if vals.get('name',False): vals_line['name'] = '%s: %s' % (tools.ustr(vals['name']), tools.ustr(task_work.name) or '/') hr_anlytic_timesheet.write(cr, uid, [line_id], vals_line, {}) - return super(task,self).write(cr, uid, ids, vals, context) + + res = super(task,self).write(cr, uid, ids, vals, context) + + for task_work_id, analytic_entry in missing_analytic_entries.items(): + timeline_id = task_work_obj._create_analytic_entries(cr, uid, analytic_entry, context=context) + task_work_obj.write(cr, uid, task_work_id, {'hr_analytic_timesheet_id' : timeline_id}, context=context) + + return res class res_partner(osv.osv): diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 338437a5ecf..5dd100c6905 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -150,7 +150,20 @@ class purchase_order(osv.osv): def _get_picking_in(self, cr, uid, context=None): obj_data = self.pool.get('ir.model.data') - return obj_data.get_object_reference(cr, uid, 'stock','picking_type_in') and obj_data.get_object_reference(cr, uid, 'stock','picking_type_in')[1] or False + type_obj = self.pool.get('stock.picking.type') + user_obj = self.pool.get('res.users') + company_id = user_obj.browse(cr, uid, uid, context=context).company_id.id + pick_type = obj_data.get_object_reference(cr, uid, 'stock', 'picking_type_in') and obj_data.get_object_reference(cr, uid, 'stock', 'picking_type_in')[1] or False + if pick_type: + type = type_obj.browse(cr, uid, pick_type, context=context) + if type and type.warehouse_id and type.warehouse_id.company_id.id == company_id: + return pick_type + types = type_obj.search(cr, uid, [('code', '=', 'incoming'), ('warehouse_id.company_id', '=', company_id)], context=context) + if not types: + types = type_obj.search(cr, uid, [('code', '=', 'incoming'), ('warehouse_id', '=', False)], context=context) + if not types: + raise osv.except_osv(_('Error!'), _("Make sure you have at least an incoming picking type defined")) + return types[0] def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None): res = {} diff --git a/addons/sale/sale.py b/addons/sale/sale.py index af257f8d22e..2cbc975ae4c 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -701,7 +701,7 @@ class sale_order(osv.osv): vals = self._prepare_procurement_group(cr, uid, order, context=context) if not order.procurement_group_id: group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context) - order.write({'procurement_group_id': group_id}, context=context) + order.write({'procurement_group_id': group_id}) for line in order.order_line: #Try to fix exception procurement (possible when after a shipping exception the user choose to recreate) @@ -711,10 +711,11 @@ class sale_order(osv.osv): line.refresh() #run again procurement that are in exception in order to trigger another move proc_ids += [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')] + procurement_obj.reset_to_confirmed(cr, uid, proc_ids, context=context) elif sale_line_obj.need_procurement(cr, uid, [line.id], context=context): if (line.state == 'done') or not line.product_id: continue - vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context) + vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context) proc_id = procurement_obj.create(cr, uid, vals, context=context) proc_ids.append(proc_id) #Confirm procurement order such that rules will be applied on it diff --git a/addons/sale_journal/sale_journal.py b/addons/sale_journal/sale_journal.py index 076692cbf4a..f6e4abb6b53 100644 --- a/addons/sale_journal/sale_journal.py +++ b/addons/sale_journal/sale_journal.py @@ -61,17 +61,33 @@ class picking(osv.osv): } +class stock_move(osv.osv): + _inherit = "stock.move" + + def action_confirm(self, cr, uid, ids, context=None): + """ + Pass the invoice type to the picking from the sales order + (Should also work in case of Phantom BoMs when on explosion the original move is deleted, similar to carrier_id on delivery) + """ + procs_to_check = [] + for move in self.browse(cr, uid, ids, context=context): + if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.invoice_type_id: + procs_to_check += [move.procurement_id] + res = super(stock_move, self).action_confirm(cr, uid, ids, context=context) + pick_obj = self.pool.get("stock.picking") + for proc in procs_to_check: + pickings = list(set([x.picking_id.id for x in proc.move_ids if x.picking_id and not x.picking_id.invoice_type_id])) + if pickings: + pick_obj.write(cr, uid, pickings, {'invoice_type_id': proc.sale_line_id.order_id.invoice_type_id.id}, context=context) + return res + + class sale(osv.osv): _inherit = "sale.order" _columns = { 'invoice_type_id': fields.many2one('sale_journal.invoice.type', 'Invoice Type', help="Generate invoice based on the selected option.") } - def _prepare_order_picking(self, cr, uid, order, context=None): - result = super(sale,self)._prepare_order_picking(cr, uid, order, context=context) - result.update(invoice_type_id=order.invoice_type_id and order.invoice_type_id.id or False) - return result - def onchange_partner_id(self, cr, uid, ids, part, context=None): result = super(sale, self).onchange_partner_id(cr, uid, ids, part, context=context) if part: diff --git a/addons/sale_stock/sale_stock_view.xml b/addons/sale_stock/sale_stock_view.xml index 0d5223a03c8..404316324f5 100644 --- a/addons/sale_stock/sale_stock_view.xml +++ b/addons/sale_stock/sale_stock_view.xml @@ -38,17 +38,11 @@ + on_change="product_id_change_with_wh(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,False,name,parent.partner_id, False, False, parent.date_order, product_packaging, parent.fiscal_position, True, parent.warehouse_id, context)"/> - - - - - + diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py index 07024a2529e..c105ed6c017 100644 --- a/addons/stock/procurement.py +++ b/addons/stock/procurement.py @@ -273,7 +273,7 @@ class procurement_order(osv.osv): @param context: A standard dictionary for contextual values @return: Dictionary of values ''' - super(procurement_order, self).run_scheduler(cr, uid, use_new_cursor=use_new_cursor, context=context) + super(procurement_order, self).run_scheduler(cr, uid, use_new_cursor=use_new_cursor, company_id=company_id, context=context) if context is None: context = {} try: @@ -283,7 +283,7 @@ class procurement_order(osv.osv): move_obj = self.pool.get('stock.move') #Minimum stock rules - self._procure_orderpoint_confirm(cr, SUPERUSER_ID, use_new_cursor=False, company_id=company_id, context=context) + self._procure_orderpoint_confirm(cr, SUPERUSER_ID, use_new_cursor=use_new_cursor, company_id=company_id, context=context) #Search all confirmed stock_moves and try to assign them confirmed_ids = move_obj.search(cr, uid, [('state', '=', 'confirmed')], limit=None, order='priority desc, date_expected asc', context=context) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index ecaed0133a0..75def9b40d1 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2640,13 +2640,27 @@ class stock_inventory(osv.osv): def prepare_inventory(self, cr, uid, ids, context=None): inventory_line_obj = self.pool.get('stock.inventory.line') for inventory in self.browse(cr, uid, ids, context=context): - #clean the existing inventory lines before redoing an inventory proposal + # If there are inventory lines already (e.g. from import), respect those and set their theoretical qty line_ids = [line.id for line in inventory.line_ids] - inventory_line_obj.unlink(cr, uid, line_ids, context=context) - #compute the inventory lines and create them - vals = self._get_inventory_lines(cr, uid, inventory, context=context) - for product_line in vals: - inventory_line_obj.create(cr, uid, product_line, context=context) + if not line_ids: + #compute the inventory lines and create them + vals = self._get_inventory_lines(cr, uid, inventory, context=context) + for product_line in vals: + inventory_line_obj.create(cr, uid, product_line, context=context) + else: + # On import calculate theoretical quantity + quant_obj = self.pool.get("stock.quant") + for line in inventory.line_ids: + dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), + ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id)] + if line.package_id: + dom += [('package_id', '=', line.package_id.id)] + quants = quant_obj.search(cr, uid, dom, context=context) + tot_qty = 0 + for quant in quant_obj.browse(cr, uid, quants, context=context): + tot_qty += quant.qty + inventory_line_obj.write(cr, uid, [line.id], {'theoretical_qty': tot_qty}, context=context) + return self.write(cr, uid, ids, {'state': 'confirm', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) def _get_inventory_lines(self, cr, uid, inventory, context=None): @@ -2712,7 +2726,7 @@ class stock_inventory_line(osv.osv): 'company_id': fields.related('inventory_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, select=True, readonly=True), 'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"), 'state': fields.related('inventory_id', 'state', type='char', string='Status', readonly=True), - 'theoretical_qty': fields.float('Theoretical Quantity', readonly=True), + 'theoretical_qty': fields.float('Theoretical Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True), 'partner_id': fields.many2one('res.partner', 'Owner'), 'product_name': fields.related('product_id', 'name', type='char', string='Product Name', store={ 'product.product': (_get_product_name_change, ['name', 'default_code'], 20), diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index d923f5f6002..a95300d8744 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -1097,7 +1097,7 @@ - + @@ -1658,7 +1658,7 @@ stock.quant -
+
diff --git a/addons/stock/wizard/stock_transfer_details.py b/addons/stock/wizard/stock_transfer_details.py index 831f225ca13..17f302f6317 100644 --- a/addons/stock/wizard/stock_transfer_details.py +++ b/addons/stock/wizard/stock_transfer_details.py @@ -133,7 +133,7 @@ class stock_transfer_details_items(models.TransientModel): packop_id = fields.Many2one('stock.pack.operation', 'Operation') product_id = fields.Many2one('product.product', 'Product') product_uom_id = fields.Many2one('product.uom', 'Product Unit of Measure') - quantity = fields.Float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), default = 1.0) + quantity = fields.Float('Quantity', digits=dp.get_precision('Product Unit of Measure'), default = 1.0) package_id = fields.Many2one('stock.quant.package', 'Source package', domain="['|', ('location_id', 'child_of', sourceloc_id), ('location_id','=',False)]") lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number') sourceloc_id = fields.Many2one('stock.location', 'Source Location', required=True) diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index c5308fde462..9707263391a 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -282,17 +282,25 @@ class stock_move(osv.osv): def product_price_update_before_done(self, cr, uid, ids, context=None): product_obj = self.pool.get('product.product') + tmpl_dict = {} for move in self.browse(cr, uid, ids, context=context): #adapt standard price on incomming moves if the product cost_method is 'average' if (move.location_id.usage == 'supplier') and (move.product_id.cost_method == 'average'): product = move.product_id - product_avail = product.qty_available - if product.qty_available <= 0: + prod_tmpl_id = move.product_id.product_tmpl_id.id + qty_available = move.product_id.product_tmpl_id.qty_available + if tmpl_dict.get(prod_tmpl_id): + product_avail = qty_available + tmpl_dict[prod_tmpl_id] + else: + tmpl_dict[prod_tmpl_id] = 0 + product_avail = qty_available + if product_avail <= 0: new_std_price = move.price_unit else: # Get the standard price amount_unit = product.standard_price new_std_price = ((amount_unit * product_avail) + (move.price_unit * move.product_qty)) / (product_avail + move.product_qty) + tmpl_dict[prod_tmpl_id] += move.product_qty # Write the standard price, as SUPERUSER_ID because a warehouse manager may not have the right to write on products product_obj.write(cr, SUPERUSER_ID, [product.id], {'standard_price': new_std_price}, context=context) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 15f6e80f509..8d7f8869ae2 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -4372,7 +4372,6 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ if (!this.fields_view || !this.editable()){ return true; } - this.o2m._dirty_flag = true; var r; return _.every(this.records.records, function(record){ r = record; diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index c01baa3098f..b8ff361cb35 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -166,7 +166,7 @@ diff --git a/addons/website_mail_group/views/website_mail_group.xml b/addons/website_mail_group/views/website_mail_group.xml index b72f5f5c903..b8172e4015d 100644 --- a/addons/website_mail_group/views/website_mail_group.xml +++ b/addons/website_mail_group/views/website_mail_group.xml @@ -212,7 +212,7 @@ t-att-title="attachment.name"/> - diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index ab50ac88cec..d776db029cd 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -179,6 +179,7 @@ class website_sale(http.Controller): if search: post["search"] = search if category: + category = pool['product.public.category'].browse(cr, uid, int(category), context=context) url = "/shop/category/%s" % slug(category) style_obj = pool['product.style'] diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py index 6e71d3fa574..2fbe081d768 100644 --- a/addons/website_sale_delivery/models/sale_order.py +++ b/addons/website_sale_delivery/models/sale_order.py @@ -63,7 +63,7 @@ class SaleOrder(orm.Model): if not order: return False if all(line.product_id.type == "service" for line in order.website_order_line): - order.write({'carrier_id': None}, context=context) + order.write({'carrier_id': None}) self.pool['sale.order']._delivery_unset(cr, SUPERUSER_ID, [order.id], context=context) return True else: @@ -81,11 +81,11 @@ class SaleOrder(orm.Model): if grid_id: carrier_id = delivery_id break - order.write({'carrier_id': carrier_id}, context=context) + order.write({'carrier_id': carrier_id}) if carrier_id: - order.delivery_set(context=context) + order.delivery_set() else: - order._delivery_unset(context=context) + order._delivery_unset() return bool(carrier_id) diff --git a/doc/_themes/odoodoc/layout.html b/doc/_themes/odoodoc/layout.html index 7685d8af25f..1a68d31814a 100644 --- a/doc/_themes/odoodoc/layout.html +++ b/doc/_themes/odoodoc/layout.html @@ -32,7 +32,7 @@
{{ toctree(maxdepth=4, collapse=False, includehidden=True, - main_navbar=False, titles_only=False) }} + navbar='side', titles_only=False) }} {% if github_link %}

Edit on GitHub @@ -72,7 +72,8 @@ {% endif %}

diff --git a/doc/_themes/odoodoc/sphinx_monkeypatch.py b/doc/_themes/odoodoc/sphinx_monkeypatch.py index 13c3a305988..182fef89c8d 100644 --- a/doc/_themes/odoodoc/sphinx_monkeypatch.py +++ b/doc/_themes/odoodoc/sphinx_monkeypatch.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import sphinx.roles import sphinx.environment -from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.html import HTMLTranslator from docutils.writers.html4css1 import HTMLTranslator as DocutilsTranslator @@ -9,27 +8,17 @@ def patch(): # navify toctree (oh god) @monkey(sphinx.environment.BuildEnvironment) def resolve_toctree(old_resolve, self, *args, **kwargs): - """ If main_navbar, bootstrapify TOC to yield a navbar + """ If navbar, bootstrapify TOC to yield a navbar """ - main_navbar = kwargs.pop('main_navbar', False) + navbar = kwargs.pop('navbar', None) toc = old_resolve(self, *args, **kwargs) if toc is None: return None - navbarify(toc[0], main_navbar=main_navbar) + navbarify(toc[0], navbar=navbar) return toc - @monkey(StandaloneHTMLBuilder) - def _get_local_toctree(old_local, self, *args, **kwargs): - """ _get_local_toctree generates a documentation toctree for the local - document (?), called from handle_page - """ - # so can call toctree(main_navbar=False) - d = {'main_navbar': True} - d.update(kwargs) - return old_local(self, *args, **d) - # monkeypatch visit_table to remove border and add .table HTMLTranslator.visit_table = visit_table # disable colspec crap @@ -37,11 +26,18 @@ def patch(): # copy data- attributes straight from source to dest HTMLTranslator.starttag = starttag_data -def navbarify(node, main_navbar=False): - # add classes to toplevel - if not main_navbar: - navify([node]) - else: +def navbarify(node, navbar=None): + """ + :param node: toctree node to navbarify + :param navbar: Whether this toctree is a 'main' navbar, a 'side' navbar or + not a navbar at all + """ + if navbar == 'side': + for n in node.traverse(): + if n.tagname == 'bullet_list': + n['classes'].append('nav') + elif navbar == 'main': + # add classes to just toplevel node['classes'].extend(['nav', 'navbar-nav', 'navbar-right']) for list_item in node.children: # bullet_list @@ -52,6 +48,10 @@ def navbarify(node, main_navbar=False): # list_item # compact_paragraph # reference + # no bullet_list.list_item -> don't dropdownify + if not list_item.children[1].children: + return + list_item['classes'].append('dropdown') # list_item.compact_paragraph.reference link = list_item.children[0].children[0] @@ -59,11 +59,6 @@ def navbarify(node, main_navbar=False): link.attributes['data-toggle'] = 'dropdown' # list_item.bullet_list list_item.children[1]['classes'].append('dropdown-menu') -def navify(nodes): - for node in nodes: - if node.tagname == 'bullet_list': - node['classes'].append('nav') - navify(node.children) def visit_table(self, node): """ diff --git a/doc/_themes/odoodoc/static/style.css b/doc/_themes/odoodoc/static/style.css index 19f5270109e..7c0b8e82959 100644 --- a/doc/_themes/odoodoc/static/style.css +++ b/doc/_themes/odoodoc/static/style.css @@ -5902,8 +5902,10 @@ button.close { .panel-body:after, .modal-footer:before, .modal-footer:after, -.document-super:before, -.document-super:after, +.document-super:not(.stripe):before, +.document-super:not(.stripe):after, +.document-super.stripe:before, +.document-super.stripe:after, .document:before, .document:after { content: " "; @@ -5924,7 +5926,8 @@ button.close { .pager:after, .panel-body:after, .modal-footer:after, -.document-super:after, +.document-super:not(.stripe):after, +.document-super.stripe:after, .document:after { clear: both; } @@ -6181,35 +6184,53 @@ body { overflow: auto; position: relative; } -.document-super { +.document-super:not(.stripe) { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px; } @media (min-width: 768px) { - .document-super { + .document-super:not(.stripe) { width: 750px; } } @media (min-width: 992px) { - .document-super { + .document-super:not(.stripe) { width: 970px; } } @media (min-width: 1200px) { - .document-super { + .document-super:not(.stripe) { width: 1170px; } } -.document-super > .navbar-header, -.document-super > .navbar-collapse { +.document-super:not(.stripe) > .navbar-header, +.document-super:not(.stripe) > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { - .document-super > .navbar-header, - .document-super > .navbar-collapse { + .document-super:not(.stripe) > .navbar-header, + .document-super:not(.stripe) > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.document-super.stripe { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +.document-super.stripe > .navbar-header, +.document-super.stripe > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .document-super.stripe > .navbar-header, + .document-super.stripe > .navbar-collapse { margin-right: 0; margin-left: 0; } @@ -6296,7 +6317,7 @@ body { -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; } -.docs-nav .navbar-nav > li > a:after { +.docs-nav .navbar-nav > li.dropdown > a:after { content: " "; display: inline-block; width: 0; @@ -6307,7 +6328,7 @@ body { border-right: 4px solid transparent; border-left: 4px solid transparent; } -.docs-nav .navbar-nav > li .dropdown-menu > li.current > a.current { +.docs-nav .navbar-nav > li.dropdown .dropdown-menu > li.current > a.current { background-color: #a24689; color: white; } @@ -6709,6 +6730,74 @@ td.field-body > ul { .descclassname { opacity: 0.5; } +.index-tree ul, +.index-tree li { + list-style: none; + padding: 0; + margin: 0; +} +.index-tree > ul > li { + margin-bottom: 0.5em; +} +.index-tree > ul > li > a { + font-family: Lato, Arial, sans-serif; + font-weight: 500; + line-height: 1.1; + color: inherit; + margin-top: 20px; + margin-bottom: 10px; + font-size: 24px; + text-align: center; +} +.index-tree > ul > li > a small, +.index-tree > ul > li > a .small { + font-weight: normal; + line-height: 1; + color: #777777; +} +.index-tree > ul > li > a small, +.index-tree > ul > li > a .small { + font-size: 65%; +} +.index-tree > ul > li > ul { + display: -webkit-flex; + display: flex; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; +} +.index-tree > ul > li > ul > li { + width: 50%; +} +.index-tree > ul > li > ul > li a { + font-family: Lato, Arial, sans-serif; + font-weight: 500; + line-height: 1.1; + color: inherit; + margin-top: 10px; + margin-bottom: 10px; + font-size: 18px; + padding-left: 10%; + padding-right: 10%; +} +.index-tree > ul > li > ul > li a small, +.index-tree > ul > li > ul > li a .small { + font-weight: normal; + line-height: 1; + color: #777777; +} +.index-tree > ul > li > ul > li a small, +.index-tree > ul > li > ul > li a .small { + font-size: 75%; +} +.index-tree > ul > li a { + display: block; + color: #a24689 !important; + padding: 0.3em 0; + margin: 3px !important; +} +.index-tree > ul > li a:hover { + background-color: #eeeeee; +} .stripe .section { margin-bottom: 2em; } diff --git a/doc/_themes/odoodoc/static/style.less b/doc/_themes/odoodoc/static/style.less index 719fa6f10e3..ea53cd95e85 100644 --- a/doc/_themes/odoodoc/static/style.less +++ b/doc/_themes/odoodoc/static/style.less @@ -25,9 +25,12 @@ body { position: relative; } -.document-super { +.document-super:not(.stripe) { .container(); } +.document-super.stripe { + .container-fluid(); +} .document { .make-row(); @@ -89,20 +92,22 @@ body { opacity: 0; .transition(all 0.3s ease-out); } - > a:after { - content: " "; - display: inline-block; - width: 0; - height: 0; - margin-left: 5px; - vertical-align: middle; - border-top: 4px solid @gray-light; - border-right: 4px solid transparent; - border-left: 4px solid transparent; - } - .dropdown-menu > li.current > a.current { - background-color: @brand-primary; - color: white; + &.dropdown { + > a:after { + content: " "; + display: inline-block; + width: 0; + height: 0; + margin-left: 5px; + vertical-align: middle; + border-top: 4px solid @gray-light; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + } + .dropdown-menu > li.current > a.current { + background-color: @brand-primary; + color: white; + } } } /* version switcher */ @@ -547,6 +552,44 @@ td.field-body { opacity: 0.5; } + +.index-tree { + ul, li { + list-style: none; + padding: 0; + margin: 0; + } + > ul > li { + margin-bottom: 0.5em; + > a { + .h3(); + text-align: center; + } + > ul { + display: -webkit-flex; + display: flex; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; + > li { + width: 50%; + a { + .h4(); + padding-left: 10%; + padding-right: 10%; + } + } + } + a { + display: block; + color: @link-color !important; + padding: 0.3em 0; + margin: 3px !important; + &:hover { + background-color: @gray-lighter; + } + } + } +} // STRIPE-STYLE PAGES .stripe { .section { diff --git a/doc/modules/api_integration.rst b/doc/api_integration.rst similarity index 99% rename from doc/modules/api_integration.rst rename to doc/api_integration.rst index 21d0163ba46..12c5b56737e 100644 --- a/doc/modules/api_integration.rst +++ b/doc/api_integration.rst @@ -1,7 +1,7 @@ :classes: stripe =========== -Odoo as API +Web Service =========== Odoo is mostly extended internally via modules, but much of its features and @@ -250,8 +250,8 @@ the login. Calling methods =============== -The second — and most generally useful — is ``xmlrpc/2/object`` which is used -to call methods of odoo models via the ``execute_kw`` RPC function. +The second endpoint is ``xmlrpc/2/object``, is used to call methods of odoo +models via the ``execute_kw`` RPC function. Each call to ``execute_kw`` takes the following parameters: diff --git a/doc/howtos/backend.rst b/doc/howtos/backend.rst index bb300ae51c8..bbf173cb5c4 100644 --- a/doc/howtos/backend.rst +++ b/doc/howtos/backend.rst @@ -1384,7 +1384,7 @@ access rights are limited. Here is an example of a rule that prevents the deletion of leads that are not in state ``cancel``. Notice that the value of the field ``groups`` must follow -the same convention as the method ``write`` of the ORM. +the same convention as the method :meth:`~openerp.models.Model.write` of the ORM. .. code-block:: xml diff --git a/doc/images/view-on-github.png b/doc/images/view-on-github.png deleted file mode 100644 index 65a8d42363d..00000000000 Binary files a/doc/images/view-on-github.png and /dev/null differ diff --git a/doc/index.rst b/doc/index.rst index 3c1696020d6..56f58b6aa0d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -4,38 +4,17 @@ odoo developer documentation Welcome to the Odoo developer documentation. -This documentation is incomplete and may contain errors, if you wish to -contribute, every page should have a :guilabel:`View on Github` link: - -.. image:: images/view-on-github.* - :align: center - -Through this link you can edit documents and submit changes for review using -`github's web interface -`_. -Contributions are welcome and appreciated. - .. todo:: what's the documentation's license? -The documentation is currently organized in four sections: - -* :doc:`tutorials`, aimed at introducing the primary areas of developing Odoo - modules -* :doc:`reference`, which ought be the complete and canonical documentation - for Odoo subsystems -* :doc:`modules`, documenting useful specialized modules and integration - methods (and currently empty) - -.. hidden toctree w/o titlesonly otherwise the titlesonly "sticks" to - in-document toctrees and we can't have a toctree showing both "sibling" - pages and current document sections - +.. rst-class:: index-tree +.. titlesonly breaks level 3 (~in-document) toc of left navbar, so use + maxdepth instead .. toctree:: - :hidden: + :maxdepth: 2 tutorials reference - modules + api_integration .. ifconfig:: todo_include_todos diff --git a/doc/modules.rst b/doc/modules.rst deleted file mode 100644 index cb5ed8d6ca1..00000000000 --- a/doc/modules.rst +++ /dev/null @@ -1,8 +0,0 @@ -============== -Module Objects -============== - -.. toctree:: - :titlesonly: - - modules/api_integration diff --git a/doc/reference/cmdline.rst b/doc/reference/cmdline.rst index 32c1ef48729..1ec7d96ae9d 100644 --- a/doc/reference/cmdline.rst +++ b/doc/reference/cmdline.rst @@ -71,7 +71,7 @@ Scaffolding is available via the :command:`odoo.py scaffold` subcommand. .. option:: -t