Merge branch '11.0' into 11.0-test

This commit is contained in:
Jared Kipe
2018-12-09 11:47:56 -08:00
18 changed files with 375 additions and 2 deletions

View File

@@ -0,0 +1 @@
from . import wizard

View File

@@ -0,0 +1,30 @@
{
'name': 'HR Expense Change',
'author': 'Hibou Corp. <hello@hibou.io>',
'version': '11.0.1.0.0',
'category': 'Employees',
'sequence': 95,
'summary': 'Technical foundation for changing expenses.',
'description': """
Technical foundation for changing expenses.
Creates wizard and permissions for making expense changes that can be
handled by other individual modules.
This module implements, as examples, how to change the Date fields.
Abstractly, individual 'changes' should come from specific 'fields' or capability
modules that handle the consequences of changing that field in whatever state the
the invoice is currently in.
""",
'website': 'https://hibou.io/',
'depends': [
'hr_expense',
],
'data': [
'wizard/expense_change_views.xml',
],
'installable': True,
'application': False,
}

View File

@@ -0,0 +1 @@
from . import test_expense_change

View File

@@ -0,0 +1,20 @@
from odoo.addons.hr_expense.tests.test_expenses import TestCheckJournalEntry
class TestExpenseChange(TestCheckJournalEntry):
def test_expense_change_basic(self):
# posts expense and gets move ready at self.expense.account_move_id.id
self.test_journal_entry()
self.assertEqual(self.expense.expense_line_ids.date, self.expense.account_move_id.date)
ctx = {'active_model': 'hr.expense', 'active_ids': self.expense.expense_line_ids.ids}
change = self.env['hr.expense.change'].with_context(ctx).create({})
self.assertEqual(change.date, self.expense.expense_line_ids.date)
change_date = '2018-01-01'
change.write({'date': change_date})
change.affect_change()
self.assertEqual(change_date, self.expense.expense_line_ids.date)
self.assertEqual(change_date, self.expense.account_move_id.date)

View File

@@ -0,0 +1 @@
from . import expense_change

View File

@@ -0,0 +1,55 @@
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class ExpenseChangeWizard(models.TransientModel):
_name = 'hr.expense.change'
_description = 'Expense Change'
expense_id = fields.Many2one('hr.expense', string='Expense', readonly=True, required=True)
expense_company_id = fields.Many2one('res.company', related='expense_id.company_id')
date = fields.Date(string='Expense Date')
@api.model
def default_get(self, fields):
rec = super(ExpenseChangeWizard, self).default_get(fields)
context = dict(self._context or {})
active_model = context.get('active_model')
active_ids = context.get('active_ids')
# Checks on context parameters
if not active_model or not active_ids:
raise UserError(
_("Programmation error: wizard action executed without active_model or active_ids in context."))
if active_model != 'hr.expense':
raise UserError(_(
"Programmation error: the expected model for this action is 'hr.expense'. The provided one is '%d'.") % active_model)
# Checks on received expense records
expense = self.env[active_model].browse(active_ids)
if len(expense) != 1:
raise UserError(_("Expense Change expects only one expense."))
rec.update({
'expense_id': expense.id,
'date': expense.date,
})
return rec
def _new_expense_vals(self):
vals = {}
if self.expense_id.date != self.date:
vals['date'] = self.date
return vals
@api.multi
def affect_change(self):
self.ensure_one()
vals = self._new_expense_vals()
old_date = self.expense_id.date
if vals:
self.expense_id.write(vals)
if 'date' in vals and self.expense_id.sheet_id.account_move_id:
self.expense_id.sheet_id.account_move_id.write({'date': vals['date']})
self.expense_id.sheet_id.account_move_id.line_ids\
.filtered(lambda l: l.date_maturity == old_date).write({'date_maturity': vals['date']})
return True

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="hr_expense_change_form" model="ir.ui.view">
<field name="name">Expense Change</field>
<field name="model">hr.expense.change</field>
<field name="arch" type="xml">
<form string="Expense Change">
<group>
<group name="group_left">
<field name="expense_id" invisible="1"/>
<field name="expense_company_id" invisible="1"/>
<field name="date"/>
</group>
<group name="group_right"/>
</group>
<footer>
<button name="affect_change" string="Change" type="object" class="btn-primary"/>
<button string="Cancel" class="btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_view_hr_expense_change" model="ir.actions.act_window">
<field name="name">Expense Change Wizard</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr.expense.change</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!-- Add Button to Existing Forms -->
<record id="hr_expense_form_view_inherit" model="ir.ui.view">
<field name="name">hr.expense.form.inherit</field>
<field name="model">hr.expense</field>
<field name="inherit_id" ref="hr_expense.hr_expense_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='state']" position="before">
<button name="%(action_view_hr_expense_change)d" string="Change"
type="action" class="btn-secondary"
attrs="{'invisible': [('state', 'in', ('draft', 'refused'))]}"
context="{'default_expense_id': active_id}"
groups="account.group_account_manager" />
</xpath>
</field>
</record>
<record id="view_hr_expense_sheet_form_inherit" model="ir.ui.view">
<field name="name">hr.expense.sheet.form.inherit</field>
<field name="model">hr.expense.sheet</field>
<field name="inherit_id" ref="hr_expense.view_hr_expense_sheet_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='expense_line_ids']/tree" position="inside">
<field name="id" invisible="1"/>
<button name="%(action_view_hr_expense_change)d" string="Change" icon="fa-exchange"
type="action"
attrs="{'invisible': [('state', 'in', ('draft', 'refused'))]}"
context="{'default_expense_id': id}"
groups="account.group_account_manager" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import wizard

View File

@@ -0,0 +1,23 @@
{
'name': 'HR Expense Change - Analytic',
'author': 'Hibou Corp. <hello@hibou.io>',
'version': '11.0.1.0.0',
'category': 'Employees',
'sequence': 96,
'summary': 'Change Analytic Account on Expense.',
'description': """
Adds fields and functionality to change the analytic account on expense
and subsequent documents.
""",
'website': 'https://hibou.io/',
'depends': [
'hr_expense_change',
'analytic',
],
'data': [
'wizard/expense_change_views.xml',
],
'installable': True,
'application': False,
}

View File

@@ -0,0 +1 @@
from . import test_expense_change

View File

@@ -0,0 +1,33 @@
from odoo.addons.hr_expense_change.tests.test_expense_change import TestExpenseChange
class TestWizard(TestExpenseChange):
def test_expense_change_basic(self):
self.analytic_account = self.env['account.analytic.account'].create({
'name': 'test account',
})
self.analytic_account2 = self.env['account.analytic.account'].create({
'name': 'test account2',
})
self.expense.expense_line_ids.write({'analytic_account_id': False})
super(TestWizard, self).test_expense_change_basic()
# Tests Adding an Analytic Account
self.assertFalse(self.expense.expense_line_ids.analytic_account_id)
ctx = {'active_model': 'hr.expense', 'active_ids': self.expense.expense_line_ids.ids}
change = self.env['hr.expense.change'].with_context(ctx).create({})
change.analytic_account_id = self.analytic_account
change.affect_change()
self.assertEqual(self.expense.expense_line_ids.analytic_account_id, self.analytic_account)
# Tests Changing
change.analytic_account_id = self.analytic_account2
change.affect_change()
self.assertEqual(self.expense.expense_line_ids.analytic_account_id, self.analytic_account2)
# Tests Removing
change.analytic_account_id = False
change.affect_change()
self.assertFalse(self.expense.expense_line_ids.analytic_account_id)

View File

@@ -0,0 +1 @@
from . import expense_change

View File

@@ -0,0 +1,57 @@
from odoo import api, fields, models, _
class ExpenseChangeWizard(models.TransientModel):
_inherit = 'hr.expense.change'
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account')
analytic_account_warning = fields.Char(string='Analytic Account Warning', compute='_compute_analytic_warning')
@api.model
def default_get(self, fields):
rec = super(ExpenseChangeWizard, self).default_get(fields)
expense = self.env['hr.expense'].browse(rec['expense_id'])
rec.update({
'analytic_account_id': expense.analytic_account_id.id,
})
return rec
@api.onchange('expense_id', 'analytic_account_id')
@api.multi
def _compute_analytic_warning(self):
self.ensure_one()
expenses = self._find_expenses_to_write_analytic(self.expense_id.analytic_account_id.id)
if len(expenses) <= 1:
self.analytic_account_warning = ''
else:
other_expenses = expenses - self.expense_id
self.analytic_account_warning = '%d other expenses will be changed. (%s)' % \
(len(other_expenses), ', '.join(other_expenses.mapped('name')))
@api.multi
def affect_change(self):
old_analytic_id = self.expense_id.analytic_account_id.id
res = super(ExpenseChangeWizard, self).affect_change()
self._affect_analytic_change(old_analytic_id)
return res
def _find_expenses_to_write_analytic(self, old_analytic_id):
if self.analytic_account_id.id == old_analytic_id:
return []
# Essentially, if you have a move, you must write all related expenses and lines.
if not self.expense_id.sheet_id.account_move_id:
return self.expense_id
return self.expense_id.sheet_id.expense_line_ids\
.filtered(lambda l: l.analytic_account_id.id == old_analytic_id)
def _affect_analytic_change(self, old_analytic_id):
expenses_to_affect = self._find_expenses_to_write_analytic(old_analytic_id)
if expenses_to_affect:
expenses_to_affect.write({'analytic_account_id': self.analytic_account_id.id})
lines_to_affect = self.expense_id.sheet_id.account_move_id \
.line_ids.filtered(lambda l: l.analytic_account_id.id == old_analytic_id and l.debit)
lines_to_affect.write({'analytic_account_id': self.analytic_account_id.id})
lines_to_affect.create_analytic_lines()

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="hr_expense_change_form_inherit" model="ir.ui.view">
<field name="name">hr.expense.change.form.inherit</field>
<field name="model">hr.expense.change</field>
<field name="inherit_id" ref="hr_expense_change.hr_expense_change_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='group_right']" position="inside">
<field name="analytic_account_id" domain="[('company_id', '=', expense_company_id)]"/>
<field name="analytic_account_warning" attrs="{'invisible': [('analytic_account_warning', '=', '')]}"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -11,5 +11,6 @@
<field name="in_procure_method">make_to_stock</field>
<field name="in_to_refund" eval="True"/>
<field name="in_require_return" eval="True"/>
<field name="so_decrement_order_qty" eval="True"/>
</record>
</odoo>

View File

@@ -2,10 +2,24 @@ from odoo import api, fields, models, _
from odoo.exceptions import UserError
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
def _get_protected_fields(self):
res = super(SaleOrderLine, self)._get_protected_fields()
context = self._context or {}
if context.get('rma_done') and 'product_uom_qty' in res:
res.remove('product_uom_qty')
return res
class RMATemplate(models.Model):
_inherit = 'rma.template'
usage = fields.Selection(selection_add=[('sale_order', 'Sale Order')])
so_decrement_order_qty = fields.Boolean(string='SO Decrement Ordered Qty.',
help='When completing the RMA, the Ordered Quantity will be decremented by '
'the RMA qty.')
class RMA(models.Model):
@@ -54,6 +68,45 @@ class RMA(models.Model):
rma.partner_id = rma.sale_order_id.partner_id
rma.partner_shipping_id = rma.sale_order_id.partner_shipping_id
@api.multi
def action_done(self):
res = super(RMA, self).action_done()
res2 = self._so_action_done()
if isinstance(res, dict) and isinstance(res2, dict):
if 'warning' in res and 'warning' in res2:
res['warning'] = '\n'.join([res['warning'], res2['warning']])
return res
if 'warning' in res2:
res['warning'] = res2['warning']
return res
elif isinstance(res2, dict):
return res2
return res
def _so_action_done(self):
warnings = []
for rma in self:
if rma.template_id.so_decrement_order_qty:
for rma_line in rma.lines:
so_lines = rma.sale_order_id.order_line.filtered(lambda l: l.product_id == rma_line.product_id)
qty_remaining = rma_line.product_uom_qty
for sale_line in so_lines:
if qty_remaining == 0:
continue
sale_line_qty = sale_line.product_uom_qty
sale_line_qty = sale_line_qty - qty_remaining
if sale_line_qty < 0:
qty_remaining = abs(sale_line_qty)
sale_line_qty = 0
else:
qty_remaining = 0
sale_line.with_context(rma_done=True).write({'product_uom_qty': sale_line_qty})
if qty_remaining:
warnings.append((rma, rma.sale_order_id, rma_line, qty_remaining))
if warnings:
return {'warning': _('Could not reduce all ordered qty:\n %s' % '\n'.join(
['%s %s %s : %s' % (w[0].name, w[1].name, w[2].product_id.display_name, w[3]) for w in warnings]))}
return True
@api.multi
def action_add_so_lines(self):
@@ -74,7 +127,7 @@ class RMA(models.Model):
sale_id = self.sale_order_id.id
values = self.template_id._values_for_in_picking(self)
update = {'sale_id': sale_id, 'group_id': group_id}
update_lines = {'group_id': group_id}
update_lines = {'to_refund': self.template_id.in_to_refund, 'group_id': group_id}
return self._picking_from_values(values, update, update_lines)
lines = self.lines.filtered(lambda l: l.product_uom_qty >= 1)
@@ -98,7 +151,7 @@ class RMA(models.Model):
sale_id = self.sale_order_id.id
values = self.template_id._values_for_out_picking(self)
update = {'sale_id': sale_id, 'group_id': group_id}
update_lines = {'to_refund_so': self.template_id.in_to_refund_so, 'group_id': group_id}
update_lines = {'group_id': group_id}
return self._picking_from_values(values, update, update_lines)
lines = self.lines.filtered(lambda l: l.product_uom_qty >= 1)

View File

@@ -72,6 +72,9 @@ class TestRMASale(TestRMA):
rma.in_picking_id.do_transfer()
rma.action_done()
# Test Ordered Qty was decremented.
self.assertEqual(order.order_line.product_uom_qty, 0.0)
# Make another RMA for the same sale order
rma2 = self.env['rma.rma'].create({
'template_id': self.template_sale_return.id,

View File

@@ -1,6 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- RMA Template -->
<record id="view_rma_template_form_sale" model="ir.ui.view">
<field name="name">rma.template.form.sale</field>
<field name="model">rma.template</field>
<field name="inherit_id" ref="rma.view_rma_template_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='usage']" position="after">
<field name="so_decrement_order_qty" string="Decrement Ordered Qty" attrs="{'invisible': [('usage', '!=', 'sale_order')]}"/>
</xpath>
</field>
</record>
<!-- RMA -->
<record id="view_rma_rma_form_sale" model="ir.ui.view">
<field name="name">rma.rma.form.sale</field>