Browse Source

[ADD] Initial Commit

pull/78/head
Sreejith P 7 years ago
parent
commit
22cc381618
  1. 39
      product_multi_uom_pos/README.rst
  2. 25
      product_multi_uom_pos/__init__.py
  3. 45
      product_multi_uom_pos/__manifest__.py
  4. 3
      product_multi_uom_pos/models/__init__.py
  5. 112
      product_multi_uom_pos/models/pos_orderline.py
  6. BIN
      product_multi_uom_pos/static/description/banner.jpg
  7. BIN
      product_multi_uom_pos/static/description/cybro_logo.png
  8. 92
      product_multi_uom_pos/static/description/index.html
  9. BIN
      product_multi_uom_pos/static/description/modify_uom_button.png
  10. BIN
      product_multi_uom_pos/static/description/multi_uom_wiz.png
  11. BIN
      product_multi_uom_pos/static/description/orderlines.png
  12. 22
      product_multi_uom_pos/static/src/css/style.css
  13. BIN
      product_multi_uom_pos/static/src/img/icon.png
  14. 152
      product_multi_uom_pos/static/src/js/multi_uom.js
  15. 39
      product_multi_uom_pos/static/src/xml/pos.xml
  16. 14
      product_multi_uom_pos/views/pos_template.xml
  17. 16
      product_multi_uom_pos/views/pos_view_extended.xml

39
product_multi_uom_pos/README.rst

@ -0,0 +1,39 @@
========================
POS Product Multiple UOM
========================
Added option to update the uom of products in pos.
Installation
============
Just select it from available modules to install it, there is no need to extra installations.
Usage
=====
After installation, go to Inventory -> Settings
and enable the multiple uom option under the 'Products' section.
Known issues / Roadmap
======================
* ...
Bug Tracker
===========
Contact odoo@cybrosys.com
Contributors
------------
* Linto CT <linto@cybrosys.in>
* Shereef PT <shereef@cybrosys.in>
Maintainer
----------
This module is maintained by Cybrosys Technologies.
For support and more information, please visit https://www.cybrosys.com.

25
product_multi_uom_pos/__init__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2017-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: LINTO C T(<https://www.cybrosys.com>)
# you can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# It is forbidden to publish, distribute, sublicense, or sell copies
# of the Software or modified copies of the Software.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
# If not, see <https://www.gnu.org/licenses/>.
#
##############################################################################
import models

45
product_multi_uom_pos/__manifest__.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: LINTO C T(<https://www.cybrosys.com>)
# you can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# It is forbidden to publish, distribute, sublicense, or sell copies
# of the Software or modified copies of the Software.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
# If not, see <https://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'POS Product Multiple UOM',
'version': '10.0.1.0.0',
'category': 'Point of Sale',
'summary': 'Multiple UOM for Products',
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': 'https://www.cybrosys.com',
'depends': ['point_of_sale', 'stock'],
'data': [
'views/pos_template.xml',
'views/pos_view_extended.xml',
],
'demo': [],
'qweb': ['static/src/xml/pos.xml'],
'images': ['static/description/banner.jpg'],
'license': 'LGPL-3',
'installable': True,
'application': False,
'auto_install': False,
}

3
product_multi_uom_pos/models/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import pos_orderline

112
product_multi_uom_pos/models/pos_orderline.py

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
from odoo import _
from odoo.tools import float_is_zero
from odoo import models, fields, api
class PosOrderLinesExtended(models.Model):
_inherit = 'pos.order.line'
uom_id = fields.Many2one('product.uom', string="UOM")
@api.model
def create(self, values):
"""updating uom to orderlines"""
try:
if values.get('uom_id'):
values['uom_id'] = values['uom_id'][0]
except Exception:
values['uom_id'] = None
pass
res = super(PosOrderLinesExtended, self).create(values)
return res
class PosOrderExtended(models.Model):
_inherit = 'pos.order'
# overwriting this function because, we need to set the uom id based on the unit
# in the orderline instead of product uom, at the time of creating the stock moves
def create_picking(self):
"""Create a picking for each order and validate it."""
Picking = self.env['stock.picking']
Move = self.env['stock.move']
StockWarehouse = self.env['stock.warehouse']
for order in self:
if not order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu']):
continue
address = order.partner_id.address_get(['delivery']) or {}
picking_type = order.picking_type_id
return_pick_type = order.picking_type_id.return_picking_type_id or order.picking_type_id
order_picking = Picking
return_picking = Picking
moves = Move
location_id = order.location_id.id
if order.partner_id:
destination_id = order.partner_id.property_stock_customer.id
else:
if (not picking_type) or (not picking_type.default_location_dest_id):
customerloc, supplierloc = StockWarehouse._get_partner_locations()
destination_id = customerloc.id
else:
destination_id = picking_type.default_location_dest_id.id
if picking_type:
message = _("This transfer has been created from the point of sale session: <a href=# data-oe-model=pos.order data-oe-id=%d>%s</a>") % (order.id, order.name)
picking_vals = {
'origin': order.name,
'partner_id': address.get('delivery', False),
'date_done': order.date_order,
'picking_type_id': picking_type.id,
'company_id': order.company_id.id,
'move_type': 'direct',
'note': order.note or "",
'location_id': location_id,
'location_dest_id': destination_id,
}
pos_qty = any([x.qty > 0 for x in order.lines if x.product_id.type in ['product', 'consu']])
if pos_qty:
order_picking = Picking.create(picking_vals.copy())
order_picking.message_post(body=message)
neg_qty = any([x.qty < 0 for x in order.lines if x.product_id.type in ['product', 'consu']])
if neg_qty:
return_vals = picking_vals.copy()
return_vals.update({
'location_id': destination_id,
'location_dest_id': return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
'picking_type_id': return_pick_type.id
})
return_picking = Picking.create(return_vals)
return_picking.message_post(body=message)
for line in order.lines.filtered(lambda l: l.product_id.type in ['product', 'consu'] and not float_is_zero(l.qty, precision_rounding=l.product_id.uom_id.rounding)):
moves |= Move.create({
'name': line.name,
'product_uom': line.uom_id.id,
'picking_id': order_picking.id if line.qty >= 0 else return_picking.id,
'picking_type_id': picking_type.id if line.qty >= 0 else return_pick_type.id,
'product_id': line.product_id.id,
'product_uom_qty': abs(line.qty),
'state': 'draft',
'location_id': location_id if line.qty >= 0 else destination_id,
'location_dest_id': destination_id if line.qty >= 0 else return_pick_type != picking_type and return_pick_type.default_location_dest_id.id or location_id,
})
# prefer associating the regular order picking, not the return
order.write({'picking_id': order_picking.id or return_picking.id})
if return_picking:
order._force_picking_done(return_picking)
if order_picking:
order._force_picking_done(order_picking)
# when the pos.config has no picking_type_id set only the moves will be created
if moves and not return_picking and not order_picking:
tracked_moves = moves.filtered(lambda move: move.product_id.tracking != 'none')
untracked_moves = moves - tracked_moves
tracked_moves.action_confirm()
untracked_moves.action_assign()
moves.filtered(lambda m: m.state in ['confirmed', 'waiting']).force_assign()
moves.filtered(lambda m: m.product_id.tracking == 'none').action_done()
return True

BIN
product_multi_uom_pos/static/description/banner.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
product_multi_uom_pos/static/description/cybro_logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

92
product_multi_uom_pos/static/description/index.html

@ -0,0 +1,92 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan">POS Product Multiple UOM</h2>
<h3 class="oe_slogan"></h3>
<h4 class="oe_slogan"><a href="https://www.cybrosys.com">Cybrosys Technologies</a> </h4>
</div>
<div class="oe_row oe_spaced" style="padding-left:65px;">
<h3><p style="margin-left: 42px;"><b>Features:</b></p></h3>
<div>
<span style="color:green;"> &#9745; </span> Multiple UOM for products in POS.<br/>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<div class="oe_picture">
<h3 class="oe_slogan">Overview</h3>
<p class="oe_mt32 text-justify" style="text-align: center;">
By default, Odoo does not provide the option to update the unit of measure
of the products we select in the point of sale. We can sell products only in its
default unit. This module provides an option to update the UOM of the products
in point of sale. The stock will also be updated in the new UOM.
</p>
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<h5 class="oe_slogan"><b>New button added for opening the wizard.</b></h5>
<div style="text-align:center;">
<div class="oe_demo oe_picture oe_screenshot">
<img src="modify_uom_button.png">
</div>
</div>
<br />
<div class="oe_row oe_spaced">
<h5 class="oe_slogan"><b>This button will open a wizard which will show all the UOMs
associated with the selected product.
</b></h5>
</div>
<div class="col-md-12">
<div class="oe_row_img oe_demo oe_picture oe_screenshot">
<img src="multi_uom_wiz.png">
</div>
</div>
<div class="oe_row oe_spaced">
<p class="oe_slogan">
We can select the UOMs available for this product and update it.
</p>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="col-md-12">
<div class=" oe_demo oe_picture oe_screenshot">
<img src="orderlines.png" />
<div class="oe_demo_footer oe_centeralign" style="background-color:rgba(162, 70, 137, 0.7);">
Updated orderline
</div>
</div>
</div>
</section>
<section class="oe_container">
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help?</h2>
<div class="oe_slogan" style="margin-top:10px !important;">
<div>
<a class="btn btn-primary btn-lg mt8"
style="color: #FFFFFF !important;border-radius: 0;" href="https://www.cybrosys.com"><i
class="fa fa-envelope"></i> Email </a> <a
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;"
href="https://www.cybrosys.com/contact/"><i
class="fa fa-phone"></i> Contact Us </a> <a
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;"
href="https://www.cybrosys.com/odoo-customization-and-installation/"><i
class="fa fa-check-square"></i> Request Customization </a>
</div>
<br>
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block">
<div>
<a href="https://twitter.com/cybrosys" target="_blank"><i class="fa fa-2x fa-twitter" style="color:white;background: #00a0d1;width:35px;"></i></a></td>
<a href="https://www.linkedin.com/company/cybrosys-technologies-pvt-ltd" target="_blank"><i class="fa fa-2x fa-linkedin" style="color:white;background: #31a3d6;width:35px;padding-left: 3px;"></i></a></td>
<a href="https://www.facebook.com/cybrosystechnologies" target="_blank"><i class="fa fa-2x fa-facebook" style="color:white;background: #3b5998;width:35px;padding-left: 8px;"></i></a></td>
<a href="https://plus.google.com/106641282743045431892/about" target="_blank"><i class="fa fa-2x fa-google-plus" style="color:white;background: #c53c2c;width:35px;padding-left: 3px;"></i></a></td>
<a href="https://in.pinterest.com/cybrosys" target="_blank"><i class="fa fa-2x fa-pinterest" style="color:white;background: #ac0f18;width:35px;padding-left: 3px;"></i></a></td>
</div>
</div>
</section>

BIN
product_multi_uom_pos/static/description/modify_uom_button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
product_multi_uom_pos/static/description/multi_uom_wiz.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
product_multi_uom_pos/static/description/orderlines.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

22
product_multi_uom_pos/static/src/css/style.css

@ -0,0 +1,22 @@
.button-multi-uom{
width: 100%;
height: 32px;
font-size: 16px;
text-align: center;
border-radius: 5px;
}
.multi-uom-span {
width: 50%;
border: 1px solid;
/* vertical-align: middle; */
align-self: center;
padding: 5px 10px;
background-color: #6EC89B;
color: #fff;
LINE-HEIGHT: 2.5;
transition: all 150ms linear;
}
.popup-product-creation{
height: 50% !important;
}

BIN
product_multi_uom_pos/static/src/img/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

152
product_multi_uom_pos/static/src/js/multi_uom.js

@ -0,0 +1,152 @@
odoo.define('product_multi_uom_pos.multi_uom',function(require) {
"use strict";
var gui = require('point_of_sale.gui');
var PosBaseWidget = require('point_of_sale.BaseWidget');
var core = require('web.core');
var models = require('point_of_sale.models');
var OrderlineSuper = models.Orderline;
var pos_screens = require('point_of_sale.screens');
var MultiUomWidget = PosBaseWidget.extend({
template: 'MultiUomWidget',
init: function(parent, args) {
this._super(parent, args);
this.options = {};
this.uom_list = [];
},
events: {
'click .button.cancel': 'click_cancel',
'click .button.confirm': 'click_confirm',
},
/*function returns all the uom s in the specified category*/
get_units_by_category: function(uom_list, categ_id){
var uom_by_categ = []
for (var uom in uom_list){
if(uom_list[uom].category_id[0] == categ_id[0]){
uom_by_categ.push(uom_list[uom]);
}
}
return uom_by_categ;
},
/*Find the base price(price of the product for reference unit)*/
find_reference_unit_price: function(product, product_uom){
if(product_uom.uom_type == 'reference'){
return product.price;
}
else if(product_uom.uom_type == 'smaller'){
return (product.price * product_uom.factor);
}
else if(product_uom.uom_type == 'bigger'){
return (product.price / product_uom.factor_inv);
}
},
/*finds the latest price for the product based on the new uom selected*/
get_latest_price: function(uom, product){
var uom_by_category = this.get_units_by_category(this.pos.units_by_id, uom.category_id);
var product_uom = this.pos.units_by_id[product.uom_id[0]];
var ref_price = this.find_reference_unit_price(product, product_uom);
var ref_unit = null;
for (var i in uom_by_category){
if(uom_by_category[i].uom_type == 'reference'){
ref_unit = uom_by_category[i];
break;
}
}
if(ref_unit){
if(uom.uom_type == 'bigger'){
return (ref_price * uom.factor_inv);
}
else if(uom.uom_type == 'smaller'){
return (ref_price / uom.factor);
}
else if(uom.uom_type == 'reference'){
return ref_price;
}
}
return product.price;
},
/*Rendering the wizard*/
show: function(options){
options = options || {};
var current_uom = this.pos.units_by_id[options.uom_list[0]];
var uom_list = this.pos.units_by_id;
var uom_by_category = this.get_units_by_category(uom_list, current_uom.category_id);
this.uom_list = uom_by_category;
this.current_uom = options.uom_list[0];
this.renderElement();
},
close: function(){
if (this.pos.barcode_reader) {
this.pos.barcode_reader.restore_callbacks();
}
},
click_confirm: function(){
var self = this;
var uom = parseInt(this.$('.uom').val());
var order = self.pos.get_order();
var orderline = order.get_selected_orderline();
var selected_uom = this.pos.units_by_id[uom];
orderline.uom_id = [];
orderline.uom_id[0] = uom;
orderline.uom_id[1] = selected_uom.display_name;
/*Updating the orderlines*/
order.remove_orderline(orderline);
order.add_orderline(orderline);
var latest_price = this.get_latest_price(selected_uom, orderline.product);
order.get_selected_orderline().set_unit_price(latest_price);
this.gui.close_popup();
return;
},
click_cancel: function(){
this.gui.close_popup();
},
});
gui.define_popup({name:'multi_uom_screen', widget: MultiUomWidget});
models.Orderline = models.Orderline.extend({
/*Adding uom_id to orderline*/
initialize: function(attr,options){
OrderlineSuper.prototype.initialize.call(this, attr, options);
this.uom_id = this ? this.product.uom_id: [];
},
export_as_JSON: function() {
var result = OrderlineSuper.prototype.export_as_JSON.call(this);
result.uom_id = this.uom_id;
return result;
},
/*this function now will return the uom_id of the orderline ,
instead of the default uom_id of the product*/
get_unit: function(){
var res = OrderlineSuper.prototype.get_unit.call(this);
var unit_id = this.uom_id;
if(!unit_id){
return res;
}
unit_id = unit_id[0];
if(!this.pos){
return undefined;
}
return this.pos.units_by_id[unit_id];
}
});
pos_screens.ActionpadWidget.include({
/*opening the wizard on button click*/
renderElement: function() {
this._super();
var self = this;
this.$('.multi-uom-span').click(function(){
var orderline = self.pos.get_order().get_selected_orderline();
var options = {
'uom_list': orderline.product.uom_id
};
self.gui.show_popup('multi_uom_screen', options);
});
}
});
});

39
product_multi_uom_pos/static/src/xml/pos.xml

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-extend="ActionpadWidget">
<t t-jquery="div.actionpad" t-operation="before">
<div class="button-multi-uom">
<span class="multi-uom-span">
Modify Unit of Measure
</span>
</div>
</t>
</t>
<t t-name="MultiUomWidget">
<div class="modal-dialog multi-uom">
<div class="popup popup-product-creation">
<p class="title">Select Unit of Measure</p>
<div>
<select class="uom" style="width:35%;height:30px;border-radius:5px;">
<t t-foreach='widget.uom_list' t-as='uom'>
<option t-att-value='uom.id'
t-att-selected="((uom.id == widget.current_uom) ? true:undefined)" >
<t t-esc='uom.display_name'/>
</option>
</t>
</select>
</div>
<div class="footer">
<div class="button confirm">
Ok
</div>
<div class="button cancel">
Cancel
</div>
</div>
</div>
</div>
</t>
</templates>

14
product_multi_uom_pos/views/pos_template.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/product_multi_uom_pos/static/src/js/multi_uom.js"></script>
</xpath>
<xpath expr="//link[@id='pos-stylesheet']" position="after">
<link rel="stylesheet" href="/product_multi_uom_pos/static/src/css/style.css"/>
</xpath>
</template>
</data>
</odoo>

16
product_multi_uom_pos/views/pos_view_extended.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<!--Adding uom id to lines-->
<record id="pos_order_line_uom" model="ir.ui.view">
<field name="name">UOM for Order-line</field>
<field name="model">pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='lines']/tree/field[@name='qty']" position="after">
<field name="uom_id"/>
</xpath>
</field>
</record>
</data>
</odoo>
Loading…
Cancel
Save