[IMP] account: onboarding of the invoicing app

-added tour for invoicing
-allow to set easily the number of the first invoice/bill/refund of a journal, from the invoice screen
-small usability fixes and refactoring
This commit is contained in:
qdp-odoo
2017-07-12 17:48:43 +02:00
parent 99385d660d
commit 9d1df81af5
11 changed files with 134 additions and 19 deletions
+4
View File
@@ -319,6 +319,8 @@ class AccountJournal(models.Model):
if journal.sequence_id:
sequence = journal.sequence_id._get_current_sequence()
journal.sequence_number_next = sequence.number_next_actual
else:
journal.sequence_number_next = 1
@api.multi
def _inverse_seq_number_next(self):
@@ -341,6 +343,8 @@ class AccountJournal(models.Model):
if journal.refund_sequence_id and journal.refund_sequence:
sequence = journal.refund_sequence_id._get_current_sequence()
journal.refund_sequence_number_next = sequence.number_next_actual
else:
journal.refund_sequence_number_next = 1
@api.multi
def _inverse_refund_seq_number_next(self):
+48 -4
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import json
import re
from lxml import etree
from datetime import datetime
from dateutil.relativedelta import relativedelta
@@ -215,6 +216,7 @@ class AccountInvoice(models.Model):
name = fields.Char(string='Reference/Description', index=True,
readonly=True, states={'draft': [('readonly', False)]}, copy=False, help='The name that will be used on account move lines')
origin = fields.Char(string='Source Document',
help="Reference of the document that produced this invoice.",
readonly=True, states={'draft': [('readonly', False)]})
@@ -340,10 +342,53 @@ class AccountInvoice(models.Model):
payments_widget = fields.Text(compute='_get_payment_info_JSON')
has_outstanding = fields.Boolean(compute='_get_outstanding_info_JSON')
#fields use to set the sequence, on the first invoice of the journal
sequence_number_next = fields.Char(string='Next Number', compute="_get_sequence_prefix", inverse="_set_sequence_next")
sequence_number_next_prefix = fields.Char(string='Next Number', compute="_get_sequence_prefix")
_sql_constraints = [
('number_uniq', 'unique(number, company_id, journal_id, type)', 'Invoice Number must be unique per Company!'),
]
@api.depends('state', 'journal_id', 'date_invoice')
def _get_sequence_prefix(self):
""" computes the number that will be assigned to the first invoice/bill/refund of a journal, in order to
let the user manually change it.
"""
for invoice in self:
journal_sequence = invoice.journal_id.sequence_id
if invoice.journal_id.refund_sequence:
domain = [('type', '=', invoice.type)]
journal_sequence = invoice.type in ['in_refund', 'out_refund'] and invoice.journal_id.refund_sequence_id or invoice.journal_id.sequence_id
elif invoice.type in ['in_invoice', 'in_refund']:
domain = [('type', 'in', ['in_invoice', 'in_refund'])]
else:
domain = [('type', 'in', ['out_invoice', 'out_refund'])]
if invoice.id:
domain += [('id', '<>', invoice.id)]
domain += [('journal_id', '=', invoice.journal_id.id), ('state', 'not in', ['draft', 'cancel'])]
if (invoice.state == 'draft') and not self.search(domain, limit=1):
prefix, dummy = journal_sequence.with_context(ir_sequence_date=invoice.date_invoice)._get_prefix_suffix()
invoice.sequence_number_next_prefix = prefix
number_next = journal_sequence._get_current_sequence().number_next_actual
invoice.sequence_number_next = '%%0%sd' % journal_sequence.padding % number_next
else:
invoice.sequence_number_next_prefix = False
invoice.sequence_number_next = 'no'
@api.multi
def _set_sequence_next(self):
''' Set the number_next on the sequence related to the invoice/bill/refund'''
for invoice in self:
nxt = re.sub("[^0-9]", '', invoice.sequence_number_next or '1')
result = re.match("(0*)([0-9]+)", nxt)
journal_sequence = invoice.journal_id.refund_sequence and invoice.journal_id.refund_sequence_id or invoice.journal_id.sequence_id
if result and journal_sequence:
#use _get_current_sequence to manage the date range sequences
sequence = journal_sequence._get_current_sequence()
sequence.number_next = int(result.group(2))
@api.model
def create(self, vals):
onchanges = {
@@ -570,13 +615,12 @@ class AccountInvoice(models.Model):
date_invoice = self.date_invoice
if not date_invoice:
date_invoice = fields.Date.context_today(self)
if not self.payment_term_id:
# When no payment terms defined
self.date_due = self.date_due or self.date_invoice
else:
if self.payment_term_id:
pterm = self.payment_term_id
pterm_list = pterm.with_context(currency_id=self.company_id.currency_id.id).compute(value=1, date_ref=date_invoice)[0]
self.date_due = max(line[0] for line in pterm_list)
elif self.date_due and (date_invoice > self.date_due):
self.date_due = date_invoice
@api.multi
def action_invoice_draft(self):
+24 -12
View File
@@ -230,25 +230,30 @@
<sheet string="Vendor Bill">
<div>
<label string="Draft Bill" attrs="{'invisible': ['|',('state','!=','draft'), ('type','!=','in_invoice')]}"/>
<label string="- First Number:" attrs="{'invisible': [('sequence_number_next_prefix','=',False)]}"/>
<label string="Draft Credit Note" attrs="{'invisible': ['|',('state','!=','draft'), ('type','!=','in_refund')]}"/>
<label string="Bill " attrs="{'invisible': ['|',('state', '=', 'draft'), ('type','!=','in_invoice')]}"/>
<label string="Credit Note " attrs="{'invisible': ['|',('state', '=', 'draft'), ('type','!=','in_refund')]}"/>
<h1 class="mt0">
<field name="number" class="oe_inline" attrs="{'invisible': [('state', '=', 'draft')]}"/>
<div attrs="{'invisible': [('sequence_number_next_prefix','=',False)]}">
<field name="sequence_number_next_prefix" class="oe_inline"/>
<field name="sequence_number_next" class="oe_inline" required="1" style="width: 80px !important;"/>
</div>
</h1>
</div>
<field name="type" invisible="1"/>
<group>
<group>
<field string="Vendor" name="partner_id"
context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}"
context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1, 'default_company_type': 'company'}"
domain="[('supplier', '=', True)]"/>
<field name="reference" string="Vendor Reference"/>
</group>
<group>
<field name="origin" attrs="{'invisible': [('origin', '=', False)]}"/>
<field name="date_invoice" string="Bill Date"/>
<field name="date_due"/>
<field name="date_due" attrs="{'readonly': [('payment_term_id','!=',False)]}"/>
<field name="move_name" invisible="1"/>
<field name="currency_id" options="{'no_create': True, 'no_open': True}" groups="base.group_multi_currency"/>
<field name="company_currency_id" invisible="1"/>
@@ -346,14 +351,14 @@
<field name="name">account.invoice.form</field>
<field name="model">account.invoice</field>
<field name="arch" type="xml">
<form string="Invoice">
<form string="Invoice" class="o_invoice_form">
<header>
<button name="action_invoice_sent" type="object" string="Send by Email" attrs="{'invisible':['|',('sent','=',True), ('state', 'not in', ('open','paid'))]}" class="oe_highlight" groups="base.group_user"/>
<button name="action_invoice_sent" type="object" string="Send by Email" attrs="{'invisible':['|',('sent','=',True), ('state', 'not in', ('open','paid'))]}" class="oe_highlight o_invoice_send" groups="base.group_user"/>
<button name="invoice_print" string="Print Invoice" type="object" attrs="{'invisible':['|',('sent','=',True), ('state', 'not in', ('open','paid'))]}" class="oe_highlight" groups="base.group_user"/>
<button name="action_invoice_sent" type="object" string="Send by Email" attrs="{'invisible':['|',('sent','=',False), ('state', 'not in', ('open','paid'))]}" groups="base.group_user"/>
<button name="invoice_print" string="Print Invoice" type="object" attrs="{'invisible':['|',('sent','=',False), ('state', 'not in', ('open','paid'))]}" groups="base.group_user"/>
<button name="%(action_account_invoice_payment)d" id="account_invoice_payment_btn" type="action" states="open" string="Register Payment" groups="account.group_account_invoice" class="oe_highlight"/>
<button name="action_invoice_open" type="object" states="draft" string="Validate" class="oe_highlight" groups="account.group_account_invoice"/>
<button name="action_invoice_open" type="object" states="draft" string="Validate" class="oe_highlight o_invoice_validate" groups="account.group_account_invoice"/>
<button name="%(action_account_invoice_refund)d" type='action' string='Add Credit Note' groups="account.group_account_invoice" attrs="{'invisible': ['|',('type', '=', 'out_refund'), ('state', 'not in', ('open','paid'))]}"/>
<button name="action_invoice_draft" states="cancel" string="Reset to Draft" type="object" groups="account.group_account_invoice"/>
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="draft,open,paid"/>
@@ -365,21 +370,28 @@
<sheet string="Invoice">
<label string="Credit Note" attrs="{'invisible': ['|',('state','=','draft'), ('type','!=','out_refund')]}"/>
<h1 class="mt0">
<label string="Draft Invoice" attrs="{'invisible': ['|',('state','not in',('draft',)), ('type','!=','out_invoice')]}"/>
<label string="Draft Credit Note" attrs="{'invisible': ['|',('state','not in',('draft',)), ('type','!=','out_refund')]}"/>
<field name="number" readonly="1" class="oe_inline" attrs="{'invisible': [('state','in',('draft',))]}"/>
<div attrs="{'invisible': [('sequence_number_next_prefix','!=',False)]}">
<label string="Draft Invoice" attrs="{'invisible': ['|',('state','not in',('draft',)), ('type','!=','out_invoice')]}"/>
<label string="Draft Credit Note" attrs="{'invisible': ['|',('state','not in',('draft',)), ('type','!=','out_refund')]}"/>
<field name="number" readonly="1" class="oe_inline" attrs="{'invisible': [('state','in',('draft',))]}"/>
</div>
<div attrs="{'invisible': [('sequence_number_next_prefix','=',False)]}">
<label string="Invoice Number:"/><br/>
<field name="sequence_number_next_prefix" class="oe_inline"/>
<field name="sequence_number_next" class="oe_inline" required="1" style="width: 80px !important;"/>
</div>
</h1>
<field name="type" invisible="1"/>
<group>
<group>
<field string="Customer" name="partner_id"
context="{'search_default_customer':1, 'show_address': 1}"
options='{"always_reload": True}'
context="{'search_default_customer':1, 'show_address': 1, 'default_company_type': 'company'}"
options='{"always_reload": True, "no_quick_create": True}'
domain="[('customer', '=', True)]"/>
<field name="payment_term_id"/>
</group>
<group>
<field name="date_invoice"/>
<field name="date_due" attrs="{'readonly': [('payment_term_id','!=',False)]}"/>
<field name="move_name" invisible="1"/>
<field name="user_id" groups="base.group_user"/>
<label for="currency_id" groups="base.group_multi_currency"/>
@@ -454,6 +466,7 @@
<page string="Other Info" name="other_info">
<group>
<group>
<field name="payment_term_id"/>
<field name="fiscal_position_id" options="{'no_create': True}" />
<field name="journal_id" groups="account.group_account_user"
options="{'no_create': True}" attrs="{'readonly':[('move_name','!=',False)]}"/>
@@ -461,7 +474,6 @@
name="account_id" groups="account.group_account_manager"/>
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<field domain="[('partner_id.ref_company_ids', 'in', [company_id])]" name="partner_bank_id" invisible="1"/>
<field name="date_due" groups="base.group_no_one"/>
</group>
<group>
<field name="move_id" groups="account.group_account_manager" attrs="{'invisible': [('move_id', '=', False)]}"/>
+1 -1
View File
@@ -2066,7 +2066,7 @@
<menuitem id="menu_product_product_action" name="Products" action="product_product_action" parent="product_configuration_menu" sequence="1"/>
<menuitem id="menu_product_template_action" name="Sellable Products"
<menuitem id="menu_product_template_action" name="Products"
parent="menu_finance_receivables"
action="product.product_template_action" sequence="110"/>
+14
View File
@@ -88,6 +88,20 @@
<field name="res_model">account.analytic.account</field>
</record>
<record model="ir.ui.view" id="partner_view_short_extra">
<field name="name">partner.short.view.account</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_short_form" />
<field name="priority" eval="20"/>
<field name="arch" type="xml">
<field name="email" position="after">
<field name="property_payment_term_id" widget="selection"/>
<field name="property_account_position_id" options="{'no_create': True, 'no_open': True}"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="partner_view_buttons">
<field name="name">partner.view.buttons</field>
<field name="model">res.partner</field>
+1
View File
@@ -19,6 +19,7 @@ You could use this simplified accounting in case you work with an (external) acc
'data': [
'views/account_menuitem_views.xml',
'views/product_template_views.xml',
'views/account_invoicing_templates.xml',
],
'demo': [
],
@@ -0,0 +1,27 @@
odoo.define('account_invoicing.tour', function(require) {
"use strict";
var core = require('web.core');
var tour = require('web_tour.tour');
var _t = core._t;
tour.register('account_invoicing_tour', {
'skip_enabled': true,
}, [{
trigger: ".o_form_readonly .o_invoice_validate",
extra_trigger: '.o_invoice_form',
content: _t("Click to <b>validate your invoice.</b> A reference will be assigned to this invoice and you will not be able to modify it anymore."),
position: "right"
}, {
trigger: ".o_invoice_send",
extra_trigger: '.o_invoice_form',
content: _t("Click to <b>send the invoice by email.</b>"),
position: "bottom"
}, {
trigger: ".o_mail_send",
content: _t("Click to <b>send the invoice.</b>"),
position: "bottom"
} ]);
});
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_backend" name="account assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/account_invoicing/static/src/js/tour.js"></script>
</xpath>
</template>
</odoo>
@@ -74,7 +74,7 @@
context="{'default_model': model, 'default_body_html': body, 'default_subject': subject}"/>
</group>
<footer>
<button string="Send" attrs="{'invisible': [('is_log', '=', True)]}" name="send_mail_action" type="object" class="btn-primary"/>
<button string="Send" attrs="{'invisible': [('is_log', '=', True)]}" name="send_mail_action" type="object" class="btn-primary o_mail_send"/>
<button string="Log" attrs="{'invisible': [('is_log', '=', False)]}" name="send_mail_action" type="object" class="btn-primary"/>
<button string="Cancel" class="btn-default" special="cancel" />
+1
View File
@@ -300,6 +300,7 @@
action='action_payment_transaction'
id='payment_transaction_menu'
parent='root_payment_menu'
groups="base.group_no_one"
sequence='20' />
<!-- Payment Tokens -->
+5 -1
View File
@@ -179,7 +179,7 @@ class IrSequence(models.Model):
number_next = _update_nogap(self, self.number_increment)
return self.get_next_char(number_next)
def get_next_char(self, number_next):
def _get_prefix_suffix(self):
def _interpolate(s, d):
return (s % d) if s else ''
@@ -208,6 +208,10 @@ class IrSequence(models.Model):
interpolated_suffix = _interpolate(self.suffix, d)
except ValueError:
raise UserError(_('Invalid prefix or suffix for sequence \'%s\'') % (self.get('name')))
return interpolated_prefix, interpolated_suffix
def get_next_char(self, number_next):
interpolated_prefix, interpolated_suffix = self._get_prefix_suffix()
return interpolated_prefix + '%%0%sd' % self.padding % number_next + interpolated_suffix
def _create_date_range_seq(self, date):