123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- # -*- coding: utf-8 -*-
- from openerp import http
- from openerp.http import request
- from werkzeug.wrappers import Response
- from werkzeug.datastructures import Headers
- from datetime import datetime
- from dateutil.relativedelta import relativedelta as rd
- from dateutil.parser import parse
- from pytz import timezone
- from gzip import GzipFile
- from StringIO import StringIO as IO
- import simplejson as json
- import gzip
- import logging
- LOGGER = logging.getLogger(__name__)
- DATE_FORMAT = '%Y-%m-%d'
- GZIP_COMPRESSION_LEVEL = 9
- '''
- ___ ___ __ __ __ ___ __ __ ___ __ ___ __ __ __ __ __
- |__| | | |__) / ` / \ |\ | | |__) / \ | | |__ |__) |__ / \ |__) |__) / \ /__`
- | | | | | \__, \__/ | \| | | \ \__/ |___ |___ |___ | \ | \__/ | \ | \__/ .__/
- # Resource paths
- - /init: return json response gzip compressed contains values for sale operation
- - /create_product: create product on the fly POS and return it
- - /create_customer: create customer on the fly and return it
- - /process_sale: processing sale and return true if completed
-
- '''
- class PosSales(http.Controller):
- '''
- Get timezone
- '''
- def get_timezone(self):
- return timezone(request.context['tz'])
- '''
- Get server date
- '''
- def get_server_date(self):
- return datetime.now(self.get_timezone()).strftime(DATE_FORMAT)
- '''
- Get current user information
- '''
- def get_user(self):
- user = request.env.user
- return {
- 'id': user.id,
- 'name': user.name,
- 'displayName': user.display_name,
- 'currency': {
- 'id': user.company_id.currency_id.id,
- 'name': user.company_id.currency_id.name,
- 'displayName': user.company_id.currency_id.display_name,
- 'symbol': user.company_id.currency_id.symbol
- },
- 'company': {
- 'id': user.company_id.id,
- 'name': user.company_id.name,
- 'displayName': user.company_id.display_name
- }
- }
- '''
- Get currencies
- '''
- def get_currencies(self):
- return [{
- 'id': currency.id,
- 'name': currency.name,
- 'displayName': currency.display_name,
- 'base': currency.base,
- 'accuracy': currency.accuracy,
- 'rateSilent': currency.rate_silent,
- 'rounding': currency.rounding,
- 'symbol': currency.symbol,
- 'position': currency.position,
- 'decimalSeparator': currency.decimal_separator,
- 'decimalPlaces': currency.decimal_places,
- 'thousandsSeparator': currency.thousands_separator
- } for currency in request.env['res.currency'].search([('active', '=', True)])]
- '''
- Get all active journals
- '''
- def get_journals(self):
- return [{
- 'id': journal.id,
- 'name': journal.name,
- 'displayName': journal.display_name,
- 'code': journal.code,
- 'cashControl': journal.cash_control,
- 'type': journal.type,
- 'currency': {
- 'id': journal.currency.id,
- 'name': journal.currency.name,
- 'displayName': journal.currency.display_name
- },
- 'defaultCreditAccount': {
- 'id': journal.default_credit_account_id.id,
- 'name': journal.default_credit_account_id.name,
- 'displayName': journal.default_credit_account_id.display_name,
- 'code': journal.default_credit_account_id.code,
- 'exchangeRate': journal.default_credit_account_id.exchange_rate,
- 'foreignBalance': journal.default_credit_account_id.foreign_balance,
- 'reconcile': journal.default_credit_account_id.reconcile,
- 'debit': journal.default_credit_account_id.debit,
- 'credit': journal.default_credit_account_id.credit,
- 'currencyMode': journal.default_credit_account_id.currency_mode,
- 'companyCurrency': {
- 'id': journal.default_credit_account_id.company_currency_id.id,
- 'name': journal.default_credit_account_id.company_currency_id.name,
- 'displayName': journal.default_credit_account_id.company_currency_id.display_name,
- },
- 'currency': {
- 'id': journal.default_credit_account_id.currency_id.id,
- 'name': journal.default_credit_account_id.currency_id.name,
- 'displayName': journal.default_credit_account_id.currency_id.display_name
- },
- }
- } for journal in request.env['account.journal'].search([('type', 'in', ['bank', 'cash']), ('default_credit_account_id.currency_id', '=', False), ('active', '=', True)], order='id')]
-
- '''
- Get all active customers
- '''
- def get_customers(self):
- return [{
- 'id': customer.id,
- 'name': customer.name,
- 'displayName': customer.display_name,
- 'imageMedium': customer.image_medium,
- 'ruc': customer.ruc,
- 'phone': customer.phone,
- 'mobile': customer.mobile,
- 'email': customer.email
- } for customer in request.env['res.partner'].search([('customer', '=', True), ('active', '=', True)])]
- '''
- Get all saleable and active products
- '''
- def get_products(self):
- return [{
- 'id': product.id,
- 'name': product.name,
- 'displayName': product.display_name,
- 'ean13': product.ean13,
- 'defaultCode': product.default_code,
- 'imageMedium': product.image_medium,
- 'listPrice': product.list_price,
- 'variantCount': product.product_variant_count,
- 'quantity': 1,
- 'price': product.list_price,
- 'minimumPrice': product.minimum_price,
- 'maximumPrice': product.maximum_price,
- 'discount': 0,
- 'variants': [{
- 'id': variant.id,
- 'name': variant.name,
- 'displayName': variant.display_name,
- 'ean13': variant.ean13,
- 'defaultCode': product.default_code,
- 'imageMedium': variant.image_medium,
- 'listPrice': variant.list_price,
- 'quantity': 1,
- 'price': variant.list_price,
- 'minimumPrice': product.minimum_price,
- 'maximumPrice': product.maximum_price,
- 'discount': 0,
- } for variant in product.product_variant_ids if variant.active]
- } for product in request.env['product.template'].search([('sale_ok', '=', True), ('list_price', '>', 0), ('active', '=', True)])]
- '''
- Get all active payment terms
- '''
- def get_payment_terms(self):
- return [{
- 'id': payment_term.id,
- 'name': payment_term.name,
- 'displayName': payment_term.display_name,
- 'lines': [{
- 'id': line.id,
- 'days': line.days,
- 'days2': line.days2,
- 'value': line.value,
- 'valueAmount': line.value_amount
- } for line in payment_term.line_ids]
- } for payment_term in request.env['account.payment.term'].search([('active', '=', True)])]
- '''
- Make JSON response
- '''
- def make_json_response(self, data=None, status=200):
- return Response(json.dumps(data), status=status, content_type='application/json')
- '''
- Make GZIP to JSON response
- '''
- def make_gzip_response(self, data=None, status=200):
- gzip_buffer = IO()
- with GzipFile(mode='wb', compresslevel=GZIP_COMPRESSION_LEVEL, fileobj=gzip_buffer) as gzip_file:
- gzip_file.write(json.dumps(data))
-
- contents = gzip_buffer.getvalue()
- gzip_buffer.close()
- headers = Headers()
- headers.add('Content-Encoding', 'gzip')
- headers.add('Vary', 'Accept-Encoding')
- headers.add('Content-Length', len(contents))
- return Response(contents, status=status, headers=headers, content_type='application/json')
- '''
- '''
- def make_info_log(self, log):
- LOGGER.info(log)
- '''
- New purchase resource route
- '''
- @http.route('/eiru_sales/init', auth='user', methods=['GET'], cors='*')
- def init_sale(self, **kw):
- self.make_info_log('Sending JSON response')
-
- return self.make_gzip_response({
- 'date': self.get_server_date(),
- 'user': self.get_user(),
- 'currencies': self.get_currencies(),
- 'journals': self.get_journals(),
- 'customers': self.get_customers(),
- 'products': self.get_products(),
- 'paymentTerms': self.get_payment_terms()
- })
-
- '''
- Create customer and return data
- '''
- @http.route('/eiru_sales/create_customer', type='json', auth='user', methods=['POST'], cors='*')
- def create_customer(self, **kw):
- self.make_info_log('Creating customer')
- customer = request.env['res.partner'].create({
- 'name': kw.get('name'),
- 'ruc': kw.get('ruc'),
- 'phone': kw.get('phone'),
- 'customer': True
- })
- return {
- 'id': customer.id,
- 'name': customer.name,
- 'displayName': customer.display_name,
- 'imageMedium': customer.image_medium,
- 'phone': customer.phone,
- 'mobile': customer.mobile,
- 'email': customer.email
- }
-
- '''
- Create product and return data
- '''
- @http.route('/eiru_sales/create_product', type='json', auth='user', methods=['POST'], cors='*')
- def create_product(self, **kw):
- self.make_info_log('Creating customer')
- product = request.env['product.template'].create({
- 'name': kw.get('name'),
- 'list_price': float(kw.get('price')),
- 'ean13': kw.get('ean13')
- })
- return {
- 'id': product.id,
- 'name': product.name,
- 'displayName': product.display_name,
- 'ean13': product.ean13,
- 'imageMedium': product.image_medium,
- 'listPrice': product.list_price,
- 'variantCount': product.product_variant_count,
- 'quantity': 1,
- 'price': product.list_price,
- 'discount': 0,
- 'variants': [{
- 'id': variant.id,
- 'name': variant.name,
- 'displayName': variant.display_name,
- 'ean13': variant.ean13,
- 'imageMedium': variant.image_medium,
- 'listPrice': variant.list_price,
- 'quantity': 1,
- 'price': variant.list_price,
- 'discount': 0,
- } for variant in product.product_variant_ids if variant.active]
- }
- '''
- Get currency from journal
- '''
- def get_currency(self, journal_id):
- journal = request.env['account.journal'].browse(journal_id)
- return journal.default_credit_account_id.currency_id.id or journal.default_credit_account_id.company_currency_id.id
- '''
- Check currency in pricelist and get it
- '''
- def get_pricelist(self, currency_id):
- pricelist = request.env['product.pricelist'].search([('active', '=', True), ('type', '=', 'sale')])
- if not True in pricelist.mapped(lambda p: p.currency_id.id == currency_id):
- pricelist = pricelist[0].copy()
- pricelist.write({
- 'currency_id': currency_id
- })
- else:
- pricelist = pricelist.filtered(lambda p: p.currency_id.id == currency_id)
-
- return pricelist
- '''
- Create sale order from cart items values
- '''
- def create_sale_from_cart(self, partner_id, cart_items, date_confirm, currency_id, pricelist_id, payment_term_id=None):
- return request.env['sale.order'].create({
- 'partner_id': partner_id,
- 'order_line': [[0, False, {
- 'product_id': int(line.get('id')),
- 'product_uom_qty': float(line.get('quantity')),
- 'price_unit': float(line.get('price'))
- }] for line in cart_items],
- 'picking_policy': 'direct',
- 'date_confirm': date_confirm,
- 'currency_id': currency_id,
- 'pricelist_id': pricelist_id,
- 'payment_term': payment_term_id,
- 'state': 'draft',
- })
- '''
- Confirm sale order
- '''
- def confirm_sale_order(self, sale_order_id):
- sale_order = request.env['sale.order'].browse(sale_order_id)
- sale_order.write({
- 'state': 'manual'
- })
- return sale_order.action_button_confirm()
- '''
- Create invoice from sale order
- '''
- def create_invoice(self, sale_order_id, currency_id, date_today):
- sale_order = request.env['sale.order'].browse(sale_order_id)
- invoice_id = sale_order.action_invoice_create()
- invoice = request.env['account.invoice'].browse(invoice_id)
- for picking in sale_order.picking_ids:
- picking.force_assign()
- picking.action_done()
- date_due = parse(date_today) + rd(days=max(invoice.payment_term.line_ids.mapped(lambda x: x.days + x.days2)))
- invoice.write({
- 'currency_id': currency_id,
- 'date_invoice': date_today,
- 'date_due': date_due.strftime(DATE_FORMAT),
- 'state': 'open'
- })
- return invoice
- '''
- Create move lines
- '''
- def create_invoice_move_lines(self, invoice_id, paid_amount, date_today):
- invoice = request.env['account.invoice'].browse(invoice_id)
- invoice_move_lines = invoice._get_analytic_lines()
- decimal_precision = request.env['decimal.precision'].precision_get('Account')
- compute_taxes = request.env['account.invoice.tax'].compute(invoice)
- invoice.check_tax_lines(compute_taxes)
- invoice._recompute_tax_amount()
- invoice_move_lines += request.env['account.invoice.tax'].move_line_get(invoice.id)
- total, total_currency, invoice_move_lines = invoice.compute_invoice_totals(invoice.company_id.currency_id, invoice.reference, invoice_move_lines)
- paid_percentage = paid_amount / round(total, decimal_precision)
- distributed_percentage = -(paid_percentage / len(invoice.payment_term.line_ids))
- payment_lines = []
- for line in invoice.payment_term.line_ids:
- date_due = (parse(date_today) + rd(days=line.days + line.days2)).strftime(DATE_FORMAT)
- if paid_percentage and paid_percentage < 1.0:
- payment_lines.append([date_today, paid_percentage])
- paid_percentage = paid_amount = 0
- if date_due == date_today and line.value_amount:
- distributed_percentage = -((payment_lines[0][1] - line.value_amount) / (len(invoice.payment_term.line_ids) - 1))
- continue
-
- if line.value != 'balance':
- payment_lines.append([date_due, line.value_amount + distributed_percentage])
- continue
- payment_lines.append([date_due, line.value_amount])
- for payment_line in payment_lines:
- current_price = round(total * payment_line[1], decimal_precision)
- if current_price < 0.0:
- continue
- paid_amount += current_price
- invoice_move_lines.append({
- 'type': 'dest',
- 'name': '/',
- 'price': current_price if payment_line[1] else round(total - paid_amount, decimal_precision) or total,
- 'account_id': invoice.account_id.id,
- 'date_maturity': payment_line[0],
- 'amount_currency': invoice.company_id.currency_id.compute(payment_line[1], invoice.currency_id) if invoice.currency_id != invoice.company_id.currency_id else False,
- 'currency_id': invoice.currency_id != invoice.company_id.currency_id and invoice.currency_id.id,
- 'ref': invoice.reference
- })
-
- payment_lines = []
- return invoice_move_lines
- '''
- Create account move
- '''
- def create_account_move(self, invoice_id, invoice_move_lines):
- invoice = request.env['account.invoice'].browse(invoice_id)
- accounting_partner = request.env['res.partner']._find_accounting_partner(invoice.partner_id)
- move_line_values = [(0, 0, invoice.line_get_convert(line, accounting_partner.id, invoice.date_invoice)) for line in invoice_move_lines]
- move_line_values = invoice.group_lines(invoice_move_lines, move_line_values)
- move_line_values = invoice.finalize_invoice_move_lines(move_line_values)
- ctx = dict(request.context, lang=invoice.partner_id.lang, company_id=invoice.company_id.id)
- period = invoice.period_id
- if not period:
- period = period.with_context(ctx).find(invoice.date_invoice)[:1]
- if period:
- for line in move_line_values:
- line[2]['period_id'] = period.id
- ctx['invoice'] = invoice
- ctx_nolang = ctx.copy()
- ctx_nolang.pop('lang', None)
-
- account_move = request.env['account.move'].with_context(ctx_nolang).create({
- 'ref': invoice.reference or invoice.name,
- 'line_id': move_line_values,
- 'journal_id': invoice.journal_id.id,
- 'date': invoice.date_invoice,
- 'narration': invoice.comment,
- 'company_id': invoice.company_id.id,
- 'period_id': period.id
- })
- invoice.with_context(ctx).write({
- 'move_id': account_move.id,
- 'period_id': account_move.period_id.id,
- 'move_name': account_move.name,
- })
- account_move.post()
- return account_move
- '''
- Number to invoice
- '''
- def number_invoice(self, invoice_id):
- request.env['account.invoice'].browse(invoice_id).action_number()
- '''
- Create voucher
- '''
- def create_account_voucher(self, account_move_id, journal_id, currency_id, paid_amount):
- account_move = request.env['account.move'].browse(account_move_id)
- account_journal = request.env['account.journal'].browse(journal_id)
- account_voucher = request.env['account.voucher'].create({
- 'reference': account_move.name,
- 'type': 'receipt',
- 'journal_id': account_journal.id,
- 'company_id': account_move.company_id.id,
- 'pre_line': True,
- 'amount': paid_amount,
- 'period_id': account_move.period_id.id,
- 'date': account_move.date,
- 'partner_id': account_move.partner_id.id,
- 'account_id': account_journal.default_credit_account_id.id,
- 'currency_id': currency_id,
- 'line_cr_ids': [[0, False, {
- 'date_due': l.date_maturity,
- 'account_id': l.account_id.id,
- 'date_original': l.invoice.date_invoice,
- 'move_line_id': l.id,
- 'amount_original': abs(l.credit or l.debit or 0.0),
- 'amount_unreconciled': abs(l.amount_residual),
- 'amount': abs(l.debit) if account_move.date == l.date_maturity else 0.0,
- 'reconcile': account_move.date == l.date_maturity,
- 'currency_id': currency_id
- }] for l in account_move.line_id]
- })
- account_voucher.action_move_line_create()
- return account_voucher
- '''
- Close a invoice
- '''
- def close_invoice(self, invoice_id):
- invoice = request.env['account.invoice'].browse(invoice_id)
- if invoice.residual == 0:
- invoice.write({
- 'state': 'paid'
- })
- '''
- Create account bank statement lines
- '''
- def create_bank_statement_lines(self, account_voucher_id, reference=None):
- account_voucher = request.env['account.voucher'].browse(account_voucher_id)
- return [[0, False, {
- 'name': account_voucher.reference,
- 'amount': account_voucher.amount,
- 'partner_id': account_voucher.partner_id.id,
- 'voucher_id': account_voucher.id,
- 'journal_id': account_voucher.journal_id.id,
- 'account_id': account_voucher.account_id.id,
- 'journal_entry_id': account_voucher.move_id.id,
- 'currency_id': account_voucher.currency_id.id,
- 'ref': 'POS/' + (reference or '')
- }]]
- '''
- Create account bank statement
- '''
- def create_bank_statement(self, account_voucher_id, account_bank_statement_lines, date_today):
- account_voucher = request.env['account.voucher'].browse(account_voucher_id)
- account_bank_statement = request.env['account.bank.statement'].search([('journal_id', '=', account_voucher.journal_id.id), ('date', '=', date_today)])
- account_bank_statement_values = {
- 'date': date_today,
- 'user_id': request.env.user.id,
- 'journal_id': account_voucher.journal_id.id,
- 'period_id': account_voucher.period_id.id,
- 'line_ids': account_bank_statement_lines,
- 'state': 'open' if account_voucher.journal_id.type == 'cash' else 'draft'
- }
- if account_bank_statement:
- size = len(account_bank_statement)
- if size == 1:
- account_bank_statement.write(account_bank_statement_values)
- else:
- account_bank_statement[size - 1].write(account_bank_statement_values)
- else:
- account_bank_statement.create(account_bank_statement_values)
- return account_bank_statement
- '''
- Process sale
- '''
- @http.route('/eiru_sales/process_sale', type='json', auth='user', methods=['POST'], cors='*')
- def process_sale(self, **kw):
- self.make_info_log('Processing sale...')
- # Get date
- date_now = datetime.now(self.get_timezone()).strftime(DATE_FORMAT)
- self.make_info_log('[OK] Getting date')
- # Get currency
- currency_id = self.get_currency(kw.get('journalId'))
- self.make_info_log('[OK] Getting journal')
- # Get Pricelist
- pricelist = self.get_pricelist(currency_id)
- self.make_info_log('[OK] Getting product pricelist')
- # Create sale order
- sale_order = self.create_sale_from_cart(kw.get('customerId'), kw.get('items'), date_now, currency_id, pricelist.id, kw.get('paymentTermId'))
- self.make_info_log('[OK] Creating sale order')
- # Check if budget
- if kw.get('mode', 'sale') != 'sale':
- return {
- 'process': True
- }
- # Confirm sale
- self.confirm_sale_order(sale_order.id)
- self.make_info_log('[OK] Confirm sale order')
- # Create invoice
- invoice = self.create_invoice(sale_order.id, currency_id, date_now)
- self.make_info_log('[OK] Creating invoice')
- # Create invoice move lines
- invoice_move_lines = self.create_invoice_move_lines(invoice.id, float(kw.get('payment')), date_now)
- self.make_info_log('[OK] Creating invoice move lines')
- # Create account move
- account_move = self.create_account_move(invoice.id, invoice_move_lines)
- self.make_info_log('[OK] Creating account move')
- # Number invoice
- self.number_invoice(invoice.id)
- self.make_info_log('[OK] Number invoice')
- # Create account voucher
- account_voucher = self.create_account_voucher(account_move.id, kw.get('journalId'), currency_id, float(kw.get('payment')))
- self.make_info_log('[OK] Creating account voucher')
- # Close invoice
- self.close_invoice(invoice.id)
- self.make_info_log('[OK] Closing invoice')
- # Create account bank statement lines
- account_bank_statement_lines = self.create_bank_statement_lines(account_voucher.id)
- self.make_info_log('[OK] Creating account bank statement lines')
- # Create bank statement
- self.create_bank_statement(account_voucher.id, account_bank_statement_lines, date_now)
- self.make_info_log('[OK] Creating account bank statement')
- return {
- 'process': True
- }
|