main.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. # -*- coding: utf-8 -*-
  2. from openerp import http
  3. from openerp.http import request
  4. from werkzeug.wrappers import Response
  5. from werkzeug.datastructures import Headers
  6. from datetime import datetime
  7. from dateutil.relativedelta import relativedelta as rd
  8. from dateutil.parser import parse
  9. from pytz import timezone
  10. from gzip import GzipFile
  11. import simplejson as json
  12. import gzip
  13. import logging
  14. try:
  15. from cStringIO import StringIO as IO
  16. except ImportError:
  17. from StringIO import StringIO as IO
  18. LOGGER = logging.getLogger(__name__)
  19. DATE_FORMAT = '%Y-%m-%d'
  20. DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
  21. GZIP_COMPRESSION_LEVEL = 9
  22. '''
  23. ___ ___ __ __ __ ___ __ __ ___ __ ___ __ __ __ __ __
  24. |__| | | |__) / ` / \ |\ | | |__) / \ | | |__ |__) |__ / \ |__) |__) / \ /__`
  25. | | | | | \__, \__/ | \| | | \ \__/ |___ |___ |___ | \ | \__/ | \ | \__/ .__/
  26. # Resource paths
  27. - /init: return json response gzip compressed contains values for sale operation
  28. - /create_product: create product on the fly POS and return it
  29. - /create_customer: create customer on the fly and return it
  30. - /process_sale: processing sale and return true if completed
  31. '''
  32. class PosSales(http.Controller):
  33. '''
  34. Get timezone
  35. '''
  36. def get_timezone(self):
  37. return timezone(request.context['tz'])
  38. '''
  39. Get server date
  40. '''
  41. def get_server_datetime(self):
  42. return datetime.now(self.get_timezone()).strftime(DATETIME_FORMAT)
  43. '''
  44. Check if module is installed
  45. '''
  46. def is_module_installed(self, module_name):
  47. domain = [('name', '=', module_name), ('state', '=', 'installed')]
  48. module = request.env['ir.module.module'].search(domain)
  49. return len(module) != 0
  50. '''
  51. Get current user information
  52. '''
  53. def get_user(self):
  54. user = request.env.user
  55. return {
  56. 'id': user.id,
  57. 'name': user.display_name,
  58. 'company': {
  59. 'id': user.company_id.id,
  60. 'name': user.company_id.display_name,
  61. 'phone': user.company_id.phone or None,
  62. 'city': user.company_id.city or None
  63. }
  64. }
  65. '''
  66. Get currencies from configured journals
  67. '''
  68. def get_currencies_from_journals(self):
  69. domain = [('type', 'in', ['bank', 'cash']), ('active', '=', True)]
  70. currencies = []
  71. for journal in request.env['account.journal'].search(domain):
  72. currency = journal.currency or journal.company_id.currency_id
  73. currencies.append({
  74. 'id': currency.id,
  75. 'name': currency.display_name,
  76. 'base': currency.base,
  77. 'symbol': currency.symbol,
  78. 'position': currency.position,
  79. 'decimalSeparator': currency.decimal_separator,
  80. 'decimalPlaces': currency.decimal_places,
  81. 'thousandsSeparator': currency.thousands_separator
  82. })
  83. return {c['id']:c for c in currencies}.values()
  84. '''
  85. Get all active journals
  86. '''
  87. def get_journals(self):
  88. domain = [('type', 'in', ['bank', 'cash']), ('active', '=', True)]
  89. return [{
  90. 'id': journal.id,
  91. 'name': journal.display_name,
  92. 'code': journal.code,
  93. 'type': journal.type,
  94. 'currencyId': journal.currency.id or journal.company_id.currency_id.id
  95. } for journal in request.env['account.journal'].search(domain, order='id')]
  96. '''
  97. Get banks
  98. '''
  99. def get_banks(self):
  100. banks = []
  101. if self.is_module_installed('eiru_bank_payments_references'):
  102. banks = [
  103. {
  104. 'id': bank.id,
  105. 'name': bank.display_name
  106. } for bank in request.env['res.bank'].search([('active', '=', True)])
  107. ]
  108. return banks
  109. '''
  110. Get bank payment types
  111. '''
  112. def get_bank_payment_types(self):
  113. bank_types = []
  114. if self.is_module_installed('eiru_bank_payments_references'):
  115. bank_types = [
  116. {
  117. 'id': type.id,
  118. 'name': type.display_name,
  119. 'code': type.code,
  120. 'defaultState': type.default_state
  121. } for type in request.env['res.bank.payments.type'].search([('is_receipt', '=', True)])
  122. ]
  123. return bank_types
  124. '''
  125. Get all active customers
  126. '''
  127. def get_customers(self):
  128. domain = [('customer', '=', True), ('active', '=', True)]
  129. return [
  130. {
  131. 'id': customer.id,
  132. 'name': customer.display_name,
  133. 'image': customer.image_small,
  134. 'ruc': customer.ruc or None,
  135. 'phone': customer.phone or None,
  136. 'mobile': customer.mobile or None,
  137. 'email': customer.email or None
  138. } for customer in request.env['res.partner'].search(domain)
  139. ]
  140. '''
  141. Get all saleable and active products
  142. '''
  143. def get_products(self):
  144. domain = [('sale_ok', '=', True), ('list_price', '>', 0), ('active', '=', True)]
  145. return [
  146. {
  147. 'id': product.id,
  148. 'name': product.display_name,
  149. 'ean13': product.ean13,
  150. 'defaultCode': product.default_code,
  151. 'image': product.image_small,
  152. 'listPrice': product.list_price,
  153. 'variantCount': product.product_variant_count,
  154. 'quantity': 1,
  155. 'price': product.list_price,
  156. 'minimumPrice': product.minimum_price,
  157. 'maximumPrice': product.maximum_price,
  158. 'discount': 0,
  159. 'variants': [{
  160. 'id': variant.id,
  161. 'name': variant.display_name,
  162. 'ean13': variant.ean13,
  163. 'defaultCode': product.default_code,
  164. 'image': variant.image_medium,
  165. 'listPrice': variant.list_price,
  166. 'quantity': 1,
  167. 'price': variant.list_price,
  168. 'minimumPrice': product.minimum_price,
  169. 'maximumPrice': product.maximum_price,
  170. 'discount': 0,
  171. } for variant in product.product_variant_ids if variant.active]
  172. } for product in request.env['product.template'].search(domain)
  173. ]
  174. '''
  175. Get all active payment terms
  176. '''
  177. def get_payment_terms(self):
  178. domain = [('active', '=', True)]
  179. return [
  180. {
  181. 'id': payment_term.id,
  182. 'name': payment_term.display_name,
  183. 'lines': [{
  184. 'id': line.id,
  185. 'days': line.days,
  186. 'days2': line.days2,
  187. 'value': line.value,
  188. 'valueAmount': line.value_amount
  189. } for line in payment_term.line_ids]
  190. } for payment_term in request.env['account.payment.term'].search(domain)
  191. ]
  192. '''
  193. Make JSON response
  194. '''
  195. def make_json_response(self, data=None, status=200):
  196. return Response(json.dumps(data), status=status, content_type='application/json')
  197. '''
  198. Make GZIP to JSON response
  199. '''
  200. def make_gzip_response(self, data=None, status=200):
  201. gzip_buffer = IO()
  202. with GzipFile(mode='wb', compresslevel=GZIP_COMPRESSION_LEVEL, fileobj=gzip_buffer) as gzip_file:
  203. gzip_file.write(json.dumps(data))
  204. contents = gzip_buffer.getvalue()
  205. gzip_buffer.close()
  206. headers = Headers()
  207. headers.add('Content-Encoding', 'gzip')
  208. headers.add('Vary', 'Accept-Encoding')
  209. headers.add('Content-Length', len(contents))
  210. return Response(contents, status=status, headers=headers, content_type='application/json')
  211. '''
  212. '''
  213. def make_info_log(self, log):
  214. LOGGER.info(log)
  215. '''
  216. New purchase resource route
  217. '''
  218. @http.route('/eiru_sales/init', auth='user', methods=['GET'], cors='*')
  219. def init_sale(self, **kw):
  220. self.make_info_log('Sending JSON response')
  221. return self.make_gzip_response(
  222. {
  223. 'date': self.get_server_datetime(),
  224. 'user': self.get_user(),
  225. 'currencies': self.get_currencies_from_journals(),
  226. 'journals': self.get_journals(),
  227. 'customers': self.get_customers(),
  228. 'products': self.get_products(),
  229. 'paymentTerms': self.get_payment_terms(),
  230. 'banks': self.get_banks(),
  231. 'bankPaymentTypes': self.get_bank_payment_types()
  232. }
  233. )
  234. '''
  235. Create customer and return data
  236. '''
  237. @http.route('/eiru_sales/create_customer', type='json', auth='user', methods=['POST'], cors='*')
  238. def create_customer(self, **kw):
  239. self.make_info_log('Creating customer')
  240. customer = request.env['res.partner'].create({
  241. 'name': kw.get('name'),
  242. 'ruc': kw.get('ruc'),
  243. 'mobile': kw.get('mobile'),
  244. 'customer': True
  245. })
  246. return {
  247. 'id': customer.id,
  248. 'name': customer.display_name,
  249. 'image': customer.image_small,
  250. 'ruc': customer.ruc or None,
  251. 'phone': customer.phone or None,
  252. 'mobile': customer.mobile or None,
  253. 'email': customer.email or None
  254. }
  255. '''
  256. Create product and return data
  257. '''
  258. @http.route('/eiru_sales/create_product', type='json', auth='user', methods=['POST'], cors='*')
  259. def create_product(self, **kw):
  260. self.make_info_log('Creating customer')
  261. product = request.env['product.template'].create({
  262. 'name': kw.get('name'),
  263. 'list_price': float(kw.get('price')),
  264. 'ean13': kw.get('ean13')
  265. })
  266. return {
  267. 'id': product.id,
  268. 'name': product.display_name,
  269. 'ean13': product.ean13,
  270. 'image': product.image_small,
  271. 'listPrice': product.list_price,
  272. 'variantCount': product.product_variant_count,
  273. 'quantity': 1,
  274. 'price': product.list_price,
  275. 'discount': 0,
  276. 'variants': [{
  277. 'id': variant.id,
  278. 'name': variant.display_name,
  279. 'ean13': variant.ean13,
  280. 'image': variant.image_small,
  281. 'listPrice': variant.list_price,
  282. 'quantity': 1,
  283. 'price': variant.list_price,
  284. 'discount': 0,
  285. } for variant in product.product_variant_ids if variant.active]
  286. }
  287. '''
  288. Get currency from journal
  289. '''
  290. def get_currency(self, journal_id):
  291. journal = request.env['account.journal'].browse(journal_id)
  292. return journal.default_credit_account_id.currency_id.id or journal.default_credit_account_id.company_currency_id.id
  293. '''
  294. Check currency in pricelist and get it
  295. '''
  296. def get_pricelist(self, currency_id):
  297. pricelist = request.env['product.pricelist'].search([('active', '=', True), ('type', '=', 'sale')])
  298. if not True in pricelist.mapped(lambda p: p.currency_id.id == currency_id):
  299. pricelist = pricelist[0].copy()
  300. pricelist.write({
  301. 'currency_id': currency_id
  302. })
  303. else:
  304. pricelist = pricelist.filtered(lambda p: p.currency_id.id == currency_id)
  305. return pricelist
  306. '''
  307. Create sale order from cart items values
  308. '''
  309. def create_sale_from_cart(self, partner_id, cart_items, date_confirm, currency_id, pricelist_id, payment_term_id=None):
  310. return request.env['sale.order'].create({
  311. 'partner_id': partner_id,
  312. 'order_line': [[0, False, {
  313. 'product_id': int(line.get('id')),
  314. 'product_uom_qty': float(line.get('quantity')),
  315. 'price_unit': float(line.get('price'))
  316. }] for line in cart_items],
  317. 'picking_policy': 'direct',
  318. 'date_confirm': date_confirm,
  319. 'currency_id': currency_id,
  320. 'pricelist_id': pricelist_id,
  321. 'payment_term': payment_term_id,
  322. 'state': 'draft',
  323. })
  324. '''
  325. Confirm sale order
  326. '''
  327. def confirm_sale_order(self, sale_order_id):
  328. sale_order = request.env['sale.order'].browse(sale_order_id)
  329. sale_order.write({
  330. 'state': 'manual'
  331. })
  332. return sale_order.action_button_confirm()
  333. '''
  334. Create invoice from sale order
  335. '''
  336. def create_invoice(self, sale_order_id, currency_id, date_today):
  337. sale_order = request.env['sale.order'].browse(sale_order_id)
  338. invoice_id = sale_order.action_invoice_create()
  339. invoice = request.env['account.invoice'].browse(invoice_id)
  340. for picking in sale_order.picking_ids:
  341. picking.force_assign()
  342. picking.action_done()
  343. date_due = parse(date_today) + rd(days=max(invoice.payment_term.line_ids.mapped(lambda x: x.days + x.days2)))
  344. invoice.write({
  345. 'currency_id': currency_id,
  346. 'date_invoice': date_today,
  347. 'date_due': date_due.strftime(DATE_FORMAT),
  348. 'state': 'open'
  349. })
  350. return invoice
  351. '''
  352. Create move lines
  353. '''
  354. def create_invoice_move_lines(self, invoice_id, paid_amount, date_today):
  355. invoice = request.env['account.invoice'].browse(invoice_id)
  356. invoice_move_lines = invoice._get_analytic_lines()
  357. decimal_precision = request.env['decimal.precision'].precision_get('Account')
  358. compute_taxes = request.env['account.invoice.tax'].compute(invoice)
  359. invoice.check_tax_lines(compute_taxes)
  360. invoice._recompute_tax_amount()
  361. invoice_move_lines += request.env['account.invoice.tax'].move_line_get(invoice.id)
  362. total, total_currency, invoice_move_lines = invoice.compute_invoice_totals(invoice.company_id.currency_id, invoice.reference, invoice_move_lines)
  363. paid_percentage = paid_amount / round(total, decimal_precision)
  364. distributed_percentage = -(paid_percentage / len(invoice.payment_term.line_ids))
  365. payment_lines = []
  366. for line in invoice.payment_term.line_ids:
  367. date_due = (parse(date_today) + rd(days=line.days + line.days2)).strftime(DATE_FORMAT)
  368. if paid_percentage and paid_percentage < 1.0:
  369. payment_lines.append([date_today, paid_percentage])
  370. paid_percentage = paid_amount = 0
  371. if date_due == date_today and line.value_amount:
  372. distributed_percentage = -((payment_lines[0][1] - line.value_amount) / (len(invoice.payment_term.line_ids) - 1))
  373. continue
  374. if line.value != 'balance':
  375. payment_lines.append([date_due, line.value_amount + distributed_percentage])
  376. continue
  377. payment_lines.append([date_due, line.value_amount])
  378. for payment_line in payment_lines:
  379. current_price = round(total * payment_line[1], decimal_precision)
  380. if current_price < 0.0:
  381. continue
  382. paid_amount += current_price
  383. invoice_move_lines.append({
  384. 'type': 'dest',
  385. 'name': '/',
  386. 'price': current_price if payment_line[1] else round(total - paid_amount, decimal_precision) or total,
  387. 'account_id': invoice.account_id.id,
  388. 'date_maturity': payment_line[0],
  389. '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,
  390. 'currency_id': invoice.currency_id != invoice.company_id.currency_id and invoice.currency_id.id,
  391. 'ref': invoice.reference
  392. })
  393. payment_lines = []
  394. return invoice_move_lines
  395. '''
  396. Create account move
  397. '''
  398. def create_account_move(self, invoice_id, invoice_move_lines):
  399. invoice = request.env['account.invoice'].browse(invoice_id)
  400. accounting_partner = request.env['res.partner']._find_accounting_partner(invoice.partner_id)
  401. move_line_values = [(0, 0, invoice.line_get_convert(line, accounting_partner.id, invoice.date_invoice)) for line in invoice_move_lines]
  402. move_line_values = invoice.group_lines(invoice_move_lines, move_line_values)
  403. move_line_values = invoice.finalize_invoice_move_lines(move_line_values)
  404. ctx = dict(request.context, lang=invoice.partner_id.lang, company_id=invoice.company_id.id)
  405. period = invoice.period_id
  406. if not period:
  407. period = period.with_context(ctx).find(invoice.date_invoice)[:1]
  408. if period:
  409. for line in move_line_values:
  410. line[2]['period_id'] = period.id
  411. ctx['invoice'] = invoice
  412. ctx_nolang = ctx.copy()
  413. ctx_nolang.pop('lang', None)
  414. account_move = request.env['account.move'].with_context(ctx_nolang).create({
  415. 'ref': invoice.reference or invoice.name,
  416. 'line_id': move_line_values,
  417. 'journal_id': invoice.journal_id.id,
  418. 'date': invoice.date_invoice,
  419. 'narration': invoice.comment,
  420. 'company_id': invoice.company_id.id,
  421. 'period_id': period.id
  422. })
  423. invoice.with_context(ctx).write({
  424. 'move_id': account_move.id,
  425. 'period_id': account_move.period_id.id,
  426. 'move_name': account_move.name,
  427. })
  428. account_move.post()
  429. return account_move
  430. '''
  431. Number to invoice
  432. '''
  433. def number_invoice(self, invoice_id):
  434. request.env['account.invoice'].browse(invoice_id).action_number()
  435. '''
  436. Create voucher
  437. '''
  438. def create_account_voucher(self, account_move_id, journal_id, currency_id, paid_amount):
  439. account_move = request.env['account.move'].browse(account_move_id)
  440. account_journal = request.env['account.journal'].browse(journal_id)
  441. account_voucher = request.env['account.voucher'].create({
  442. 'reference': account_move.name,
  443. 'type': 'receipt',
  444. 'journal_id': account_journal.id,
  445. 'company_id': account_move.company_id.id,
  446. 'pre_line': True,
  447. 'amount': paid_amount,
  448. 'period_id': account_move.period_id.id,
  449. 'date': account_move.date,
  450. 'partner_id': account_move.partner_id.id,
  451. 'account_id': account_journal.default_credit_account_id.id,
  452. 'currency_id': currency_id,
  453. 'line_cr_ids': [[0, False, {
  454. 'date_due': l.date_maturity,
  455. 'account_id': l.account_id.id,
  456. 'date_original': l.invoice.date_invoice,
  457. 'move_line_id': l.id,
  458. 'amount_original': abs(l.credit or l.debit or 0.0),
  459. 'amount_unreconciled': abs(l.amount_residual),
  460. 'amount': abs(l.debit) if account_move.date == l.date_maturity else 0.0,
  461. 'reconcile': account_move.date == l.date_maturity,
  462. 'currency_id': currency_id
  463. }] for l in account_move.line_id]
  464. })
  465. account_voucher.action_move_line_create()
  466. return account_voucher
  467. '''
  468. Close a invoice
  469. '''
  470. def close_invoice(self, invoice_id):
  471. invoice = request.env['account.invoice'].browse(invoice_id)
  472. if invoice.residual == 0:
  473. invoice.write({
  474. 'state': 'paid'
  475. })
  476. '''
  477. Create account bank statement lines
  478. '''
  479. def create_bank_statement_lines(self, account_voucher_id, reference=None):
  480. account_voucher = request.env['account.voucher'].browse(account_voucher_id)
  481. return [[0, False, {
  482. 'name': account_voucher.reference,
  483. 'amount': account_voucher.amount,
  484. 'partner_id': account_voucher.partner_id.id,
  485. 'voucher_id': account_voucher.id,
  486. 'journal_id': account_voucher.journal_id.id,
  487. 'account_id': account_voucher.account_id.id,
  488. 'journal_entry_id': account_voucher.move_id.id,
  489. 'currency_id': account_voucher.currency_id.id,
  490. 'ref': 'POS/' + (reference or '')
  491. }]]
  492. '''
  493. Create account bank statement
  494. '''
  495. def create_bank_statement(self, account_voucher_id, account_bank_statement_lines, date_today):
  496. account_voucher = request.env['account.voucher'].browse(account_voucher_id)
  497. account_bank_statement = request.env['account.bank.statement'].search([('journal_id', '=', account_voucher.journal_id.id), ('date', '=', date_today)])
  498. account_bank_statement_values = {
  499. 'date': date_today,
  500. 'user_id': request.env.user.id,
  501. 'journal_id': account_voucher.journal_id.id,
  502. 'period_id': account_voucher.period_id.id,
  503. 'line_ids': account_bank_statement_lines,
  504. 'state': 'open' if account_voucher.journal_id.type == 'cash' else 'draft'
  505. }
  506. if account_bank_statement:
  507. size = len(account_bank_statement)
  508. if size == 1:
  509. account_bank_statement.write(account_bank_statement_values)
  510. else:
  511. account_bank_statement[size - 1].write(account_bank_statement_values)
  512. else:
  513. account_bank_statement.create(account_bank_statement_values)
  514. return account_bank_statement
  515. '''
  516. Process sale
  517. '''
  518. @http.route('/eiru_sales/process_sale', type='json', auth='user', methods=['POST'], cors='*')
  519. def process_sale(self, **kw):
  520. self.make_info_log('Processing sale...')
  521. # Get date
  522. date_now = datetime.now(self.get_timezone()).strftime(DATE_FORMAT)
  523. self.make_info_log('[OK] Getting date')
  524. # Get currency
  525. currency_id = self.get_currency(kw.get('journalId'))
  526. self.make_info_log('[OK] Getting journal')
  527. # Get Pricelist
  528. pricelist = self.get_pricelist(currency_id)
  529. self.make_info_log('[OK] Getting product pricelist')
  530. # Create sale order
  531. sale_order = self.create_sale_from_cart(kw.get('customerId'), kw.get('items'), date_now, currency_id, pricelist.id, kw.get('paymentTermId'))
  532. self.make_info_log('[OK] Creating sale order')
  533. # Check if budget
  534. if kw.get('mode', 'sale') != 'sale':
  535. return {
  536. 'process': True
  537. }
  538. # Confirm sale
  539. self.confirm_sale_order(sale_order.id)
  540. self.make_info_log('[OK] Confirm sale order')
  541. # Create invoice
  542. invoice = self.create_invoice(sale_order.id, currency_id, date_now)
  543. self.make_info_log('[OK] Creating invoice')
  544. # Create invoice move lines
  545. invoice_move_lines = self.create_invoice_move_lines(invoice.id, float(kw.get('payment')), date_now)
  546. self.make_info_log('[OK] Creating invoice move lines')
  547. # Create account move
  548. account_move = self.create_account_move(invoice.id, invoice_move_lines)
  549. self.make_info_log('[OK] Creating account move')
  550. # Number invoice
  551. self.number_invoice(invoice.id)
  552. self.make_info_log('[OK] Number invoice')
  553. # Create account voucher
  554. account_voucher = self.create_account_voucher(account_move.id, kw.get('journalId'), currency_id, float(kw.get('payment')))
  555. self.make_info_log('[OK] Creating account voucher')
  556. # Close invoice
  557. self.close_invoice(invoice.id)
  558. self.make_info_log('[OK] Closing invoice')
  559. # Create account bank statement lines
  560. account_bank_statement_lines = self.create_bank_statement_lines(account_voucher.id)
  561. self.make_info_log('[OK] Creating account bank statement lines')
  562. # Create bank statement
  563. self.create_bank_statement(account_voucher.id, account_bank_statement_lines, date_now)
  564. self.make_info_log('[OK] Creating account bank statement')
  565. return {
  566. 'process': True,
  567. 'name': sale_order.display_name,
  568. 'date': self.get_server_datetime()
  569. }