2016-11-11 18:13:31 +01:00
|
|
|
# The COPYRIGHT file at the top level of this repository contains the full
|
|
|
|
# copyright notices and license terms.
|
2016-12-03 06:10:53 +01:00
|
|
|
import sys
|
|
|
|
import traceback
|
2016-11-11 18:13:31 +01:00
|
|
|
import logging
|
2016-11-18 00:38:22 +01:00
|
|
|
from decimal import Decimal
|
2016-12-03 06:10:53 +01:00
|
|
|
from trytond.modules.sale_fedicom.service.nan_socket import Socket
|
2016-11-11 18:13:31 +01:00
|
|
|
from trytond.modules.sale_fedicom.service.messages.init_session \
|
|
|
|
import InitSession
|
|
|
|
from trytond.modules.sale_fedicom.service.messages.close_session \
|
|
|
|
import CloseSession
|
|
|
|
from trytond.modules.sale_fedicom.service.messages.order import Order
|
|
|
|
from trytond.modules.sale_fedicom.service.messages.order_line import OrderLine
|
|
|
|
from trytond.modules.sale_fedicom.service.messages.finish_order \
|
|
|
|
import FinishOrder
|
2016-11-18 00:38:22 +01:00
|
|
|
from trytond.modules.sale_fedicom.service.messages.incidence_header \
|
|
|
|
import IncidenceHeader
|
|
|
|
from trytond.modules.sale_fedicom.service.messages.incidence_order_line \
|
|
|
|
import IncidenceOrderLine
|
2016-11-11 18:13:31 +01:00
|
|
|
from trytond.model import fields
|
2016-11-18 00:38:22 +01:00
|
|
|
from trytond.pool import Pool, PoolMeta
|
2016-12-03 06:10:53 +01:00
|
|
|
from trytond.transaction import Transaction
|
2016-11-11 18:13:31 +01:00
|
|
|
|
2017-01-11 12:45:02 +01:00
|
|
|
__all__ = ['Party', 'Product', 'Purchase', 'PurchaseLine', 'FedicomLog']
|
2016-11-11 18:13:31 +01:00
|
|
|
|
2016-11-18 00:38:22 +01:00
|
|
|
_ZERO = Decimal(0)
|
|
|
|
|
2016-11-11 18:13:31 +01:00
|
|
|
|
|
|
|
class Party:
|
|
|
|
__name__ = 'party.party'
|
|
|
|
__metaclass__ = PoolMeta
|
|
|
|
|
2016-11-18 00:38:22 +01:00
|
|
|
fedicom_host = fields.Char('Fedicom Host')
|
|
|
|
fedicom_port = fields.Integer('Fedicom Port')
|
|
|
|
fedicom_timeout = fields.Integer('Fedicom Timeout')
|
2016-11-11 18:13:31 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Product:
|
|
|
|
__name__ = 'product.product'
|
|
|
|
__metaclass__ = PoolMeta
|
|
|
|
|
2017-01-11 12:45:02 +01:00
|
|
|
def get_supplier_code(self, supplier):
|
|
|
|
code = self.base_code
|
|
|
|
if code:
|
|
|
|
return code
|
|
|
|
for product_supplier in self.template.product_suppliers:
|
|
|
|
if product_supplier.party == supplier and product_supplier.code:
|
|
|
|
code = product_supplier.code
|
|
|
|
return code
|
|
|
|
return code
|
2016-11-11 18:13:31 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Purchase:
|
|
|
|
__name__ = 'purchase.purchase'
|
|
|
|
__metaclass__ = PoolMeta
|
|
|
|
|
2016-12-03 06:10:53 +01:00
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(Purchase, cls).__setup__()
|
|
|
|
cls._error_messages.update({
|
|
|
|
'error_connecting': 'Error connecting to fedicom server.',
|
|
|
|
'wrong_frame': 'frame sent does not belong to next state.',
|
2017-01-11 12:45:02 +01:00
|
|
|
'product_without_code': 'Product %(product)s without code',
|
2016-12-03 06:10:53 +01:00
|
|
|
})
|
|
|
|
|
2016-11-11 18:13:31 +01:00
|
|
|
@classmethod
|
|
|
|
def process(cls, purchases):
|
|
|
|
for purchase in purchases:
|
|
|
|
purchase.process_fedicom_purchase()
|
|
|
|
super(Purchase, cls).process(purchases)
|
|
|
|
|
|
|
|
def process_fedicom_purchase(self):
|
2016-12-03 06:10:53 +01:00
|
|
|
FedicomLog = Pool().get('fedicom.log')
|
2016-11-11 18:13:31 +01:00
|
|
|
logger = logging.getLogger('purchase_fedicom')
|
|
|
|
|
|
|
|
logger.info('Process Purchase %s From Party %s' % (
|
|
|
|
self.reference, self.party.code))
|
|
|
|
|
2016-11-18 00:38:22 +01:00
|
|
|
if not self.party.fedicom_host or not self.party.fedicom_port:
|
|
|
|
return
|
|
|
|
|
2016-12-03 06:10:53 +01:00
|
|
|
sock = Socket()
|
|
|
|
sock.socket.settimeout(self.party.fedicom_timeout or 30)
|
|
|
|
try:
|
|
|
|
sock.connect(self.party.fedicom_host, self.party.fedicom_port)
|
|
|
|
except:
|
|
|
|
exc_type, exc_value = sys.exc_info()[:2]
|
|
|
|
logger.warning("Exception connecting to fedicom server: "
|
|
|
|
"%s (%s)\n %s"
|
|
|
|
% (exc_type, exc_value, traceback.format_exc()))
|
|
|
|
with Transaction().set_user(0):
|
|
|
|
with Transaction().new_cursor():
|
|
|
|
FedicomLog.create([{
|
|
|
|
'message': self.raise_user_error('error_connecting',
|
|
|
|
raise_exception=False),
|
|
|
|
'party': self.party.id,
|
|
|
|
'purchase': self.id,
|
|
|
|
}])
|
|
|
|
Transaction().cursor.commit()
|
|
|
|
self.raise_user_error('error_connecting')
|
2016-11-11 18:13:31 +01:00
|
|
|
|
|
|
|
msg = self.send_order()
|
2016-12-03 06:10:53 +01:00
|
|
|
sock.send(msg)
|
|
|
|
data = sock.recieve()
|
|
|
|
sock.disconnect()
|
2016-11-11 18:13:31 +01:00
|
|
|
|
2016-11-18 00:38:22 +01:00
|
|
|
self.process_message(data)
|
2016-11-11 18:13:31 +01:00
|
|
|
|
|
|
|
def send_order(self):
|
|
|
|
msg = ""
|
|
|
|
msg += str(InitSession(self.party.fedicom_user,
|
|
|
|
self.party.fedicom_password, ''))
|
|
|
|
msg += str(Order(self.party.fedicom_user, '1'))
|
|
|
|
quantity = 0
|
|
|
|
for line in self.lines:
|
2017-01-11 12:45:02 +01:00
|
|
|
code = line.product.get_supplier_code(self.party)
|
|
|
|
if code == None:
|
|
|
|
self.raise_user_error('product_without_code', {
|
|
|
|
'product': line.product.rec_name,
|
|
|
|
})
|
|
|
|
msg += str(OrderLine(code, int(line.quantity)))
|
2016-11-11 18:13:31 +01:00
|
|
|
quantity += line.quantity
|
|
|
|
f = FinishOrder()
|
|
|
|
f.finishOrder(str(len(self.lines)), int(quantity), 0)
|
|
|
|
msg += str(f)
|
|
|
|
msg += str(CloseSession())
|
|
|
|
return msg
|
2016-11-18 00:38:22 +01:00
|
|
|
|
|
|
|
def process_message(self, msg):
|
|
|
|
logger = logging.getLogger('purchase_fedicom')
|
|
|
|
logger.info('Process Order Incidence')
|
|
|
|
|
|
|
|
msg_list = msg.split('\r\n')
|
|
|
|
i = 0
|
|
|
|
|
|
|
|
init_session = InitSession()
|
|
|
|
init_session.set_message(msg_list[i])
|
|
|
|
|
|
|
|
i = i + 1
|
|
|
|
next_message = init_session.next_state()
|
|
|
|
incidence = {}
|
|
|
|
incidence_lines = {}
|
|
|
|
while i < len(msg_list) - 1:
|
|
|
|
msg = msg_list[i]
|
|
|
|
if not msg[0:4] in next_message:
|
2016-12-03 06:10:53 +01:00
|
|
|
logger.warning("An error has occurred, "
|
|
|
|
"frame sent does not belong to next state")
|
|
|
|
with Transaction().set_user(0):
|
|
|
|
with Transaction().new_cursor():
|
|
|
|
FedicomLog.create([{
|
|
|
|
'message': self.raise_user_error('wrong_frame',
|
|
|
|
raise_exception=False),
|
|
|
|
'party': self.party.id,
|
|
|
|
'purchase': self.id,
|
|
|
|
}])
|
|
|
|
Transaction().cursor.commit()
|
|
|
|
self.raise_user_error('wrong_frame')
|
2016-11-18 00:38:22 +01:00
|
|
|
|
|
|
|
for state in next_message:
|
|
|
|
if msg.startswith(state):
|
2016-12-03 06:10:53 +01:00
|
|
|
if msg.startswith('0199'):
|
|
|
|
logger.info('Processing Close Session')
|
2016-11-18 00:38:22 +01:00
|
|
|
next_message = None
|
|
|
|
if incidence_lines:
|
|
|
|
self.process_incidence(incidence_lines)
|
|
|
|
return
|
2016-12-03 06:10:53 +01:00
|
|
|
elif msg.startswith('2010'):
|
|
|
|
logger.info('Processing Incidence Header')
|
2016-11-18 00:38:22 +01:00
|
|
|
incidence = IncidenceHeader()
|
|
|
|
incidence.set_msg(msg)
|
|
|
|
next_message = incidence.next_state()
|
|
|
|
elif msg.startswith('2015'):
|
2016-12-03 06:10:53 +01:00
|
|
|
logger.info('Processing Incidence Order Line')
|
2016-11-18 00:38:22 +01:00
|
|
|
incidence_line = IncidenceOrderLine()
|
|
|
|
incidence_line.set_msg(msg)
|
|
|
|
next_message = incidence_line.next_state()
|
2017-01-11 12:45:02 +01:00
|
|
|
product_code = incidence_line.article_code
|
2016-11-18 00:38:22 +01:00
|
|
|
incidence_lines[product_code] = \
|
2017-01-11 12:45:02 +01:00
|
|
|
(int(incidence_line.amount_not_served),
|
|
|
|
incidence_line.incidence_code)
|
2016-11-18 00:38:22 +01:00
|
|
|
else:
|
2016-12-03 06:10:53 +01:00
|
|
|
with Transaction().set_user(0):
|
|
|
|
with Transaction().new_cursor():
|
|
|
|
FedicomLog.create([{
|
|
|
|
'message': self.raise_user_error(
|
|
|
|
'wrong_frame',
|
|
|
|
raise_exception=False),
|
|
|
|
'party': self.party.id,
|
|
|
|
'purchase': self.id,
|
|
|
|
}])
|
|
|
|
Transaction().cursor.commit()
|
|
|
|
self.raise_user_error('wrong_frame')
|
2016-11-18 00:38:22 +01:00
|
|
|
|
|
|
|
i = i + 1
|
|
|
|
return
|
|
|
|
|
|
|
|
def process_incidence(self, incidence_lines):
|
|
|
|
pool = Pool()
|
|
|
|
Purchase = pool.get('purchase.purchase')
|
|
|
|
PurchaseLine = pool.get('purchase.line')
|
2016-12-03 06:10:53 +01:00
|
|
|
logger = logging.getLogger('purchase_fedicom')
|
2016-11-18 00:38:22 +01:00
|
|
|
|
|
|
|
|
2017-01-11 12:45:02 +01:00
|
|
|
# Create new purchase for missing quantities
|
|
|
|
purchase, = Purchase.copy([self], {'state': 'draft'})
|
|
|
|
print "*"*100
|
|
|
|
print incidence_lines
|
2016-11-18 00:38:22 +01:00
|
|
|
# Update purchase quantities
|
|
|
|
amount = Decimal('0.0')
|
|
|
|
for line in self.lines:
|
2017-01-11 12:45:02 +01:00
|
|
|
code = line.product.get_supplier_code(self.party).rjust(13, '0')
|
|
|
|
amount, reason = incidence_lines.get(code, (None, None))
|
|
|
|
if amount >= 0:
|
|
|
|
line.quantity = (line.quantity - amount)
|
|
|
|
line.fedicom_reply_state = reason
|
2016-11-18 00:38:22 +01:00
|
|
|
line.save()
|
2016-12-03 06:10:53 +01:00
|
|
|
else:
|
2017-01-11 12:45:02 +01:00
|
|
|
logger.info("No result por product %s" % code)
|
|
|
|
line.quantity = 0
|
|
|
|
line.description = "(%s)-%s" % (reason, line.description)
|
|
|
|
line.save()
|
2016-11-18 00:38:22 +01:00
|
|
|
amount += line.amount if line.type == 'line' else _ZERO
|
|
|
|
|
|
|
|
# Update cached fields
|
|
|
|
self.untaxed_amount_cache = amount
|
|
|
|
self.tax_amount_cache = self.get_tax_amount()
|
|
|
|
self.total_amount_cache = (
|
|
|
|
self.untaxed_amount_cache + self.tax_amount_cache)
|
|
|
|
self.save()
|
|
|
|
|
|
|
|
lines_to_delete = []
|
2017-01-11 12:45:02 +01:00
|
|
|
lines = []
|
2016-11-18 00:38:22 +01:00
|
|
|
for line in purchase.lines:
|
2017-01-11 12:45:02 +01:00
|
|
|
code = line.product.get_supplier_code(self.party).rjust(13, '0')
|
|
|
|
amount, reason = incidence_lines.get(code, (None, None))
|
|
|
|
if amount >= 0:
|
|
|
|
line.quantity = amount
|
|
|
|
line.fedicom_reply_state = reason
|
2016-11-18 00:38:22 +01:00
|
|
|
line.save()
|
2017-01-11 12:45:02 +01:00
|
|
|
lines.append(line)
|
2016-11-18 00:38:22 +01:00
|
|
|
else:
|
|
|
|
lines_to_delete.append(line)
|
2017-01-11 12:45:02 +01:00
|
|
|
|
2016-11-18 00:38:22 +01:00
|
|
|
PurchaseLine.delete(lines_to_delete)
|
2016-12-03 06:10:53 +01:00
|
|
|
|
|
|
|
|
2017-01-11 12:45:02 +01:00
|
|
|
class PurchaseLine:
|
|
|
|
__name__ = 'purchase.line'
|
|
|
|
__metaclass__ = PoolMeta
|
|
|
|
|
|
|
|
fedicom_reply_state = fields.Selection(
|
|
|
|
[
|
|
|
|
(None, ''),
|
|
|
|
('01', 'Without Stock'),
|
|
|
|
('02', 'No Serve'),
|
|
|
|
('03', 'Not Worked'),
|
|
|
|
('04', 'Unknown'),
|
|
|
|
('05', 'Drug'),
|
|
|
|
('06', 'To Order'),
|
|
|
|
('07', 'To Drop Out'),
|
|
|
|
('08', 'Pass to Warehouse'),
|
|
|
|
('09', 'New Speciality'),
|
|
|
|
('10', 'Temporal Drop Out'),
|
|
|
|
('11', 'Drop Out'),
|
|
|
|
('12', 'To Order Ok'),
|
|
|
|
('13', 'Limit Service'),
|
|
|
|
('14', 'Sanity Removed'),
|
|
|
|
], 'Fedicom Reply State'
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2016-12-03 06:10:53 +01:00
|
|
|
class FedicomLog:
|
|
|
|
__name__ = 'fedicom.log'
|
|
|
|
__metaclass__ = PoolMeta
|
|
|
|
|
|
|
|
purchase = fields.Many2One('purchase.purchase', 'Purchase')
|