[ADD][T4221] Import purchase orders via FTP

Merged Hermes requested to merge T4221 into 12.0
Compare and
1 file
+ 105
87
Preferences
File browser
Compare changes
@@ -9,26 +9,31 @@
import base64
import glob
import logging
import re
import os
import pytz
from datetime import datetime, timedelta
from ftplib import FTP
from io import BytesIO
from odoo import _, models
from odoo.exceptions import UserError
from pathlib import Path
_logger = logging.getLogger(__name__)
def connect_ftp():
ftp = FTP('10.10.15.134')
ftp.login(user='odoo', passwd='Odoo123!')
return ftp
class CustomerPurchaseOrderImporter(models.Model):
_name = 'customer.purchase.order.importer'
_description = 'Customer Purchase Order Importer'
"""The command to mount the remote folder where the import files are stored is:
sudo mount -t cifs //10.10.15.134/KrikosDMS/TEST_ORDERS/Documentos/Download/ORDERS \
/home/docker/volumes/<filestore_volume_name>/_data/filestore/<db_name>/import/purchase_orders \
-o vers=3.0,uid=1000,gid=1000,forceuid,forcegid,username=odoo,domain=lario.net,password=<password>
/home/Downloads -o vers=3.0,uid=1000,gid=1000,forceuid,forcegid,username=odoo,domain=lario.net,password=Odoo123!
To get the filestore volume name, run:
docker volume ls
@@ -40,6 +45,14 @@ class CustomerPurchaseOrderImporter(models.Model):
used for testing purposes only. The original folder will be used in production.
"""
def get_lines(self, file_txt):
lines = file_txt.split('\r\n')
if lines[-1] == '':
lines.pop()
return lines
def get_full_file_data(self, lines):
msg_list = []
address = None
@@ -1033,83 +1046,90 @@ class CustomerPurchaseOrderImporter(models.Model):
processed_path = paths['processed']
rejected_path = paths['rejected']
if not manual_import:
_logger.info('Importing PO files from ' + import_path)
# if not manual_import:
# _logger.info('Importing PO files from ' + import_path)
# files = glob.glob('{}*.*'.format(import_path))
ftp = connect_ftp()
files = glob.glob('{}*.*'.format(import_path))
ftp.cwd(import_path)
files = self.standarize_file_names(import_path, ftp)
for _file in files:
_file = self.standarize_file_name(import_path, _file)
if not _file:
continue
po_vals = {}
with open(import_path + _file, 'r', encoding='utf-8') as file:
msg_list = []
file = BytesIO()
ftp.retrbinary('RETR ' + _file, file.write)
msg_list = []
with self.env.cr.savepoint():
file_txt = file.read()
lines = file_txt.split('\n')
file.close()
with self.env.cr.savepoint():
file_txt = file.getvalue().decode('utf-8')
file_vals = self.get_full_file_data(lines)
lines = self.get_lines(file_txt)
if isinstance(file_vals, list):
partner_name = file_vals.pop(-1)
file.close()
if partner_name not in _file:
rejected_file = _file.replace('ORD_', 'ORD_{}_'.format(partner_name))
file_vals = self.get_full_file_data(lines)
os.rename(import_path + _file, import_path + rejected_file)
if isinstance(file_vals, list):
partner_name = file_vals.pop(-1)
_file = rejected_file
if partner_name not in _file:
rejected_file = _file.replace('ORD_', 'ORD_{}_'.format(partner_name))
msg_list.insert(0, _('FILE: {}\n').format(_file))
ftp.rename(import_path + _file, import_path + rejected_file)
msg_list.extend(file_vals)
not_imported.append(''.join(msg_list) + '\n')
_file = rejected_file
os.rename(import_path + _file, rejected_path + _file)
msg_list.insert(0, _('FILE: {}\n').format(_file))
continue
else:
partner = file_vals['partner']
config = self.get_config(partner)
config_type = 'basic'
if config:
for c in config:
if c.config_type:
if c.config_type != 'internal_code':
config_type = c.config_type
try:
po_vals.update(
getattr(self, 'get_config_{}_vals'.format(config_type))(file_vals)
)
except AttributeError:
_type = CustomerPurchaseOrderConfig._fields['config_type'].selection
config_type_name = dict(_type).get(config_type)
msg_list.insert(0, _('FILE: {}\n').format(_file))
msg_list.append(
_(
'~ No method has been defined for {}. Please contact '
'the developer to fix this issue.\n'
).format(_('configuration type {}').format(config_type_name))
+ '\n'
)
not_imported.append(''.join(msg_list))
continue
imported.append(_file)
msg_list.extend(file_vals)
not_imported.append(''.join(msg_list) + '\n')
po_vals.update(self._update_file_vals(file_vals))
po_vals = self._refactor_product_vals_before_import(po_vals)
PreSaleOrder.create(po_vals)
ftp.rename(import_path + _file, rejected_path + _file)
os.rename(import_path + _file, processed_path + _file)
continue
else:
partner = file_vals['partner']
config = self.get_config(partner)
config_type = 'basic'
if config:
for c in config:
if c.config_type:
if c.config_type != 'internal_code':
config_type = c.config_type
try:
po_vals.update(
getattr(self, 'get_config_{}_vals'.format(config_type))(file_vals)
)
except AttributeError:
_type = CustomerPurchaseOrderConfig._fields['config_type'].selection
config_type_name = dict(_type).get(config_type)
msg_list.insert(0, _('FILE: {}\n').format(_file))
msg_list.append(
_(
'~ No method has been defined for {}. Please contact '
'the developer to fix this issue.\n'
).format(_('configuration type {}').format(config_type_name))
+ '\n'
)
not_imported.append(''.join(msg_list))
continue
imported.append(_file)
po_vals.update(self._update_file_vals(file_vals))
po_vals = self._refactor_product_vals_before_import(po_vals)
PreSaleOrder.create(po_vals)
ftp.rename(import_path + _file, processed_path + _file)
if len(not_imported) > 0:
msg += (
@@ -1118,7 +1138,7 @@ class CustomerPurchaseOrderImporter(models.Model):
else ''
) + ''.join(sorted(not_imported))
self._create_error_file(rejected_path, msg)
# self._create_error_file(rejected_path, msg, ftp)
if len(imported) > 0:
msg += _('The following files have been imported successfully:\n') + ', '.join(sorted(imported))
@@ -1130,58 +1150,56 @@ class CustomerPurchaseOrderImporter(models.Model):
if manual_import:
raise UserError(msg)
def _create_error_file(self, path, msg):
def _create_error_file(self, path, datas, ftp):
IrAttachment = self.env['ir.attachment']
today = str(datetime.today().date())
file_name = '_not_imported_{}.txt'.format(today.replace('-', ''))
file = open(path + file_name, 'w+')
file.write(msg)
file.seek(0)
datas = base64.b64encode((file.read()).encode('utf-8'))
name = _('[{}] Rejected Purchase Order Files').format(today)
attachment_id = IrAttachment.search([('name', '=', name)])
if attachment_id:
datas += base64.b64decode(attachment_id.datas)
attachment_id.write({'datas': base64.b64encode(datas.encode('utf-8')), 'name': name})
file_name = '_not_imported_{}.txt'.format(today.replace('-', ''))
file = BytesIO(bytes(datas, 'utf-8'))
ftp.storbinary('STOR ' + file_name, file)
file.seek(0)
if not attachment_id:
IrAttachment.create(
{
'datas_fname': file_name,
'datas': datas,
'datas': base64.b64encode(file.read()),
'name': name,
'type': 'binary',
}
)
else:
attachment_id.write({'datas': datas, 'name': name})
file.close()
def standarize_file_name(self, path, old_name):
if '_not_imported_' not in old_name:
name = old_name.replace(path, '')
if name[:4].upper() == 'ORD_' and old_name[-4:].lower() == '.txt':
new_name = 'ORD_{}.txt'.format(name[4:-4])
def standarize_file_names(self, path, ftp):
files = []
os.rename(path + name, path + new_name)
old_file_names = ftp.nlst()
for old_name in old_file_names:
if '_not_imported_' not in old_name:
if old_name[:4].upper() == 'ORD_' and old_name[-4:].lower() == '.txt':
new_name = 'ORD_%s.txt' % old_name[4:-4]
return new_name
ftp.rename(path + old_name, path + new_name)
return False
files.append(new_name)
return False
return files
def _get_paths(self):
IrConfigParameter = self.env['ir.config_parameter']
import_path = IrConfigParameter.sudo().get_param('po_importer_path')
if import_path[-1] != '/':
import_path += '/'
processed_path = IrConfigParameter.sudo().get_param('po_importer_processed_path')
if processed_path[-1] != '/':
processed_path += '/'
rejected_path = import_path + 'RECHAZADOS/'
rejected_path = import_path + 'RECHAZADOS\\'
return {'import': import_path, 'processed': processed_path, 'rejected': rejected_path}