POS order to quotation in Odoo 9

Odoo 9 community does not have a button to generate from the POS (Point of Sale) Order a quotation (Sales Order), here is the following code of my module pos_2_so. Folders arborescence will have as sub folders :  model, view, static, ref the print screen :

We will start by adding a new button on the interface of the pos, in the folder static/src/xml/, we create a new file named pos_2_so.xml. Bellow is its QWeb xml code :

<?xml version="1.0" encoding="UTF-8"?>
<templates id="template">
	<t t-name="Pos2SoWidget">
            <div class="pos2sopad">
                <div id="pos-2-so-frame">
		    <button class="pos-2-so-button" id="pos-2-so-button">To Sales Order</button>
	        </div>
	    </div>
	</t>
        <t t-extend="ProductScreenWidget">
		<t t-jquery=".leftpane .window" t-operation="append">
                    <div class="placeholder-Pos2SoWidget"></div>
		</t>
	</t>
</templates>

This portion of code creates a template named pos2sopad which include only a button :

<t t-name="Pos2SoWidget">
<div class="pos2sopad">
<div id="pos-2-so-frame">
<button class="pos-2-so-button" id="pos-2-so-button">To Sales Order</button>
</div>
</div>
</t>

We need to set a placeholder by adding a div inside the template ProductScreenWidget that we will append in the div .leftpane .window class :

<t t-extend="ProductScreenWidget">
<t t-jquery=".leftpane .window" t-operation="append">
<div class="placeholder-Pos2SoWidget"></div>
</t>
</t>

We now load the QWeb file in our module by adding this code from __openerp__.py like bellow:

{
    'name' : 'POS 2 SO',
    'version' : '0.1',
    'category' : 'tools',
    'description' : """
Create SO from POS
    """,
    'author' : 'NumberSpeaks',
    'depends' : [
        'base',
        'point_of_sale',
    ],
    'data' : [
        'view/pos_2_order.xml',
        'view/pos_order_view.xml',
    ],
    'qweb': [
        'static/src/xml/pos_2_so.xml',
    ],
    'demo' : [],
    'installable' : True,
    'auto_install' : False,
}

Your template has been created, but they are still not displayable on our screen, we will need javascript for that. Here is the following javascript code located in static/src/js :

odoo.define('point_of_sale.pos_2_so', function(require) {
	"use strict";

	var PosBaseWidget = require('point_of_sale.BaseWidget');
	var gui = require('point_of_sale.gui');
	var screens = require('point_of_sale.screens');

	var Pos2SoWidget = PosBaseWidget.extend({
		template : 'Pos2SoWidget',
		init : function(parent) {
			var self = this;
			this._super(parent);
		},
		start : function() {
			var self = this;
			this.$el.find('.pos-2-so-button').click(function() {
				self.gui.show_screen('payment');
				pos2so = true;
			});
		},
	});

	screens.ProductScreenWidget.include({
		start : function() {

			var self = this;
			this._super();

			this.pos2sopad = new Pos2SoWidget(this, {});
			this.pos2sopad.replace(this.$('.placeholder-Pos2SoWidget'));

		},
	});

We will initialize the template QWeb Pos2SoWidget previously created and run a function when clicking on the button, on this example clicking on the button will display the payment screen and set pos2so to true:

var Pos2SoWidget = PosBaseWidget.extend({
	template : 'Pos2SoWidget',
	init : function(parent) {
		var self = this;
		this._super(parent);
	},
	start : function() {
		var self = this;
		this.$el.find('.pos-2-so-button').click(function() {
			self.gui.show_screen('payment');
			pos2so = true;
		});
	},
});

then replace div placeholder-pos2sowidget by the html generated by the template :

screens.ProductScreenWidget.include({
	start : function() {

		var self = this;
		this._super();

		this.pos2sopad = new Pos2SoWidget(this, {});
		this.pos2sopad.replace(this.$('.placeholder-Pos2SoWidget'));

	},
});

To include our javascript file in our web page, we have to create a new file name pos_2_so_view.xml in view folder, reference it in __openerp__.py file, ref the content of that file above :

<?xml version="1.0" encoding="utf-8"?>
<openerp>
	<data>
		<template id="pos_2_so_assets" name="pos_2_so css assets" inherit_id="point_of_sale.assets">
			<xpath expr="." position="inside">
				<link rel="stylesheet" href="/pos_2_so/static/src/css/pos_2_so.css" />
				<script type="text/javascript" src="/pos_2_so/static/src/js/pos_2_so.js"></script>
			</xpath>
		</template>
	</data>
</openerp>

After restarting Odoo and installing the new module, you should see this additional button on the screen of your point of sale:

pos 2 so button

Define schedule actions Odoo 9

Schedule actions can be set by adding this code in your xml file. This exemple will call the function schedule_action from the model hr.employee and send a notification by email if the boolean enable_send_notification is True.

<record id="ir_cron_schedule_action" model="ir.cron">
   <field name="name">Schedule actions</field>
   <field name="user_id" ref="base.user_root" />
   <field name="interval_number">1</field>
   <field name="interval_type">days</field>
   <field name="numbercall">-1</field>
   <field eval="False" name="doall" />
   <field eval="'hr.employee'" name="model" />
   <field eval="'schedule_action'" name="function" />
</record>

This model inherits hr.employee and defines 1 additional field, and 2 additional functions. send_notification is sending email based on the the template my_module.email_hr_employee_notification, see this post how to create email template with xml, schedule_action is the function called by the scheduler.

import logging
import openerp.addons.decimal_precision as dp
from datetime import datetime, date, timedelta
from openerp.exceptions import UserError
from openerp import models, fields, api, _

_logger = logging.getLogger(__name__)
    
class HrEmployee(models.Model):
    _inherit = 'hr.employee'
    
    enable_send_notification = fields.Boolean(string='Send notification')

    @api.multi
    def send_notification(self):
        self.ensure_one()
        ir_model_data = self.env['ir.model.data']
        template_obj = self.env.ref('my_module.email_hr_employee_notification')
        template_obj.send_mail(self.ids[0], force_send=True)
        return True
    
    def schedule_action(self, cr, uid, context=None):
        hr_employee_obj = self.pool.get('hr.employee')
        hr_employee_ids = self.pool.get('hr.employee').search(cr, uid, [])
        for hr_employee_id in hr_employee_ids:
            hr_employee = hr_employee_obj.browse(cr, uid, hr_employee_id , context=context)
            if hr_employee.enable_send_notification:
                hr_employee.send_notification()

Send an email using a template

mail

Create a function in your model:

@api.multi
def send_email_notification(self):
    self.ensure_one()
    ir_model_data = self.env['ir.model.data']
    template_obj = self.env.ref('abc.email_template')
    template_obj.send_mail(self.ids[0], force_send=True)
    return True

call the function by the following XML code:

<button name="action_email_notification" string="Send Email Notification" type="object" class="oe_highlight" />

Odoo 9 Email Template

mail
This is a basic email template to put in the xml file:

<record id="email_template" model="mail.template">
    <field name="name">My Email Template</field>
    <field name="email_from">no-reply@domain.com</field>
    <field name="subject">Email Template</field>
    <field name="partner_to">${object.address_home_id.id}</field>
    <field name="model_id" ref="module_name.model_model_name" />
    <field name="auto_delete" eval="True" />
    <field name="lang">${object.user_id.lang}</field>
    <field name="body_html"><![CDATA[<div>My email template</div>]]></field>
</record>

if the module name is abc and the model name is voyelle then module_name.model_model_name should be abc.model_voyelle.
${object} is the model ${object.name} will display the attribute name of the current model.

Fail2ban Odoo 9 Authentication

fail2ban

Odoo 9 community doesn’t come with autoban security. Fail2ban is an alternative to secure Odoo authentication. For more information concerning fail2ban click here

Let’s start with creating a new filter:

vi /etc/fail2ban/filter.d/odoo.conf

Paste the content from bellow code:

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf

[Definition]

failregex = INFO:openerp.addons.base.res.res_users:Login failed for db:.* login:.*\n.*INFO:werkzeug: - - \[.*\] \"POST /web/login .*\" 200 -
         - \[.*\] \"POST /web/database/(drop|duplicate|create) HTTP/2.0\"

ignoreregex =
journalmatch = _SYSTEMD_UNIT=odoo.service + _COMM=odoo

[Init]

maxlines = 2

Add those line in jail.local

[odoo]
enabled = true
port    = 443,80,8069
filter  = odoo
logpath = /var/log/syslog
maxretry = 5
bantime  = -1
findtime = 1h

Change the value of syslog in the /etc/odoo/openerp-server.conf

syslog = True

Restart Odoo then fail2ban to apply modification.