1
# -*- coding: utf-8 -*-
2
##############################################
4
# Swing Entwicklung betrieblicher Informationssysteme GmbH
5
# (<http://www.swing-system.com>)
6
# Copyright (C) ChriCar Beteiligungs- und Beratungs- GmbH
8
# 05-AUG-2010 (GK) created
10
# WARNING: This program as such is intended to be used by professional
11
# programmers who take the whole responsability of assessing all potential
12
# consequences resulting from its eventual inadequacies and bugs.
13
# End users who are looking for a ready-to-use solution with commercial
14
# garantees and support are strongly adviced to contract a Free Software
17
# This program is Free Software; you can redistribute it and/or
18
# modify it under the terms of the GNU General Public License
19
# as published by the Free Software Foundation; either version 3
20
# of the License, or (at your option) any later version.
22
# This program is distributed in the hope that it will be useful,
23
# but WITHOUT ANY WARRANTY; without even the implied warranty of
24
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
# GNU General Public License for more details.
27
# You should have received a copy of the GNU General Public License
28
# along with this program; if not, see <http://www.gnu.org/licenses/> or
29
# write to the Free Software Foundation, Inc.,
30
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32
###############################################
33
from osv import fields, osv
34
from tools.translate import _
40
class payment_type(osv.osv) :
41
_inherit = "payment.type"
42
# http://www.unece.org/trade/untdid/d00a/tred/tred4471.htm
44
{ 'charges_alloc' : fields.selection
45
([ ( '1', 'Bill back')
46
, ( '2', 'Off invoice')
47
, ( '3', 'Vendor check to customer')
48
, ( '4', 'Credit customer account')
49
, ( '5', 'Charge to be paid by vendor')
50
, ( '6', 'Charge to be paid by customer')
52
, ( '8', 'Off gross quantity invoiced')
53
, ( '9', 'Electric cost recovery factor')
54
, ('10', 'Gas cost recovery factor')
55
, ('11', 'Prior credit balance')
56
, ('12', 'Non-dutiable')
57
, ('13', 'All charges borne by payee')
58
, ('14', 'Each pay own cost')
59
, ('15', 'All charges borne by payor')
60
, ('16', 'All bank charges to be borne by applicant')
61
, ('17', 'All bank charges except confirmation commission to be borne by applicant')
62
, ('18', 'All bank charges to be borne by beneficiary')
63
, ('20', 'Amendment charges to be borne by applicant')
64
, ('21', 'Amendment charges to be borne by beneficiary')
65
, ('22', 'Discount charges to be borne by applicant')
66
, ('23', 'Discount charges to be borne by beneficiary')
67
, ('24', 'All bank charges other than those of the issuing bank to be borne by beneficiary')
68
, ('25', 'Amendment charges other than those of the issuing bank to be borne by beneficiary')
69
, ('26', 'All charges to be paid by the principal of the collection')
70
, ('27', 'All charges to be paid by the drawee of the collection')
71
, ('28', 'All charges to be borne by the drawee except those levied by the remitting bank, to be paid by principal')
72
, ('29', 'All bank charges are to be paid by the principal of the documentary credit collection')
73
, ('30', 'All bank charges to be borne by receiving bank')
74
, ('31', 'All bank charges to be borne by sending bank')
75
, ('32', 'Charges levied by a third bank')
76
, ('33', 'Information charges levied by a third bank')
77
, ('34', 'Total payment borne by patient')
78
, ('35', 'Part payment borne by patient')
79
, ('ZZZ', 'Mutually defined')
81
, 'Financial Charges Allocation'
84
_defaults = {'charges_alloc' : lambda *a: '14'}
85
# end class payment_type
88
class payment_order(osv.osv) :
89
_inherit = "payment.order"
91
def _address(self, partner, type_list):
92
for type in type_list :
93
for addr in partner.address :
94
if addr.type == type :
96
raise osv.except_osv \
98
, _('Partner "%s" has no address defined.')
103
def _u2a(self, text) :
104
if not text : return ""
107
if ord(c) < 128 : txt += c
108
elif c in unicode2ascii.EXTRA_LATIN_NAMES : txt += unicode2ascii.EXTRA_LATIN_NAMES[c]
109
elif c in unicode2ascii.UNI2ASCII_CONVERSIONS : txt += unicode2ascii.UNI2ASCII_CONVERSIONS[c]
110
elif c in unicode2ascii.EXTRA_CHARACTERS : txt += unicode2ascii.EXTRA_CHARACTERS[c]
111
elif c in unicode2ascii.FG_HACKS : txt += unicode2ascii.FG_HACKS[c]
116
def action_open(self, cr, uid, ids, *args):
118
result = super(payment_order, self).action_open(cr, uid, ids, args)
119
self.generate_edifact(cr, uid, ids, {})
121
# end def action_open
123
def get_wizard(self, type):
127
def _line(self, l, iban) :
129
s += ("SEQ++%(seq)s'" % l)
130
s += ("MOA+9:%(amount)s:%(currency)s'" % l)
131
s += ("RFF+PQ:%(move_name)s'" % l)
132
s += ("FCA+%(fca)s'" % l)
134
s += ("FII+BF+%(iban)s:%(name)s+%(bic)s:25:5'" % l)
136
s += ("FII+BF+%(account)s:%(name)s+%(blz)s:25:5::::+%(bank_country)s'" % l) # sgr12
137
s += ("NAD+BE+++%(name)s+%(street)s+%(city)s+%(zip)s+%(country)s'" % l) # sgr3
139
if l['reference'] != l['move_name'] :
140
s += ("FTX+PMD+++%(reference)s'" % l)
142
s += ("FTX+PMD+++'" % l)
146
def _region(self, order, banks, i):
147
partner_bank_obj = self.pool.get('res.partner.bank')
149
for p_bank, lines in banks.iteritems() :
150
if p_bank.state == "iban" :
151
iban = p_bank.iban.replace(" ", "").upper()
154
iban = partner_bank_obj._construct_iban(p_bank)
155
account = p_bank.acc_number
158
bic = p_bank.bank.bic.replace(" ", "").upper()
159
blz = p_bank.bank.code
160
bank_name = self._u2a(p_bank.bank.name).upper()[0:70]
161
bank_country = "" if not p_bank.bank.country else p_bank.bank.country.code
162
if [l for l in lines if l.amount <= 0.0] :
165
invoice = line.move_line_id.invoice
166
a = sum(l.amount for l in lines)
167
# XXX put maximum info here - strip CN and year
168
# XXX GK this no general solution!
169
reference = self._u2a \
172
(invoice.number, "" if not invoice.reference else (" " + invoice.reference)
176
move_name = self._u2a(", ".join(l.move_line_id.name for l in lines))[0:35]
177
p_address = self._address(line.partner_id, ['invoice', 'default', False])
180
, 'amount' : "%s" % a
181
, 'bank_name' : bank_name
182
, 'bank_country' : bank_country
184
, 'account' : account
187
, 'currency' : line.currency.name
188
, 'move_name' : move_name
189
, 'fca' : order.mode.type.charges_alloc
190
, 'name' : self._u2a(line.partner_id.name).upper()[0:35]
191
, 'street' : self._u2a(p_address.street).upper()[0:35]
192
, 'city' : self._u2a(p_address.city).upper()[0:35]
193
, 'zip' : self._u2a(p_address.zip).upper()[0:9]
194
, 'country' : p_address.country_id.code or ""
195
, 'reference' : reference
197
s += self._line(l, iban)
201
invoice = line.move_line_id.invoice
202
reference = self._u2a \
204
+ ((" " + line.communication2 if line.communication2 else ""))
205
+ ((" " + invoice.reference if invoice.reference else ""))
207
p_address = self._address(line.partner_id, ['invoice', 'default', False])
210
, 'amount' : "%s" % line.amount
211
, 'bank_name' : bank_name
212
, 'bank_country' : bank_country
214
, 'account' : account
217
, 'currency' : line.currency.name
218
, 'move_name' : self._u2a(line.move_line_id.name)[0:35]
219
, 'fca' : order.mode.type.charges_alloc
220
, 'name' : self._u2a(line.partner_id.name).upper()[0:35]
221
, 'street' : self._u2a(p_address.street).upper()[0:35]
222
, 'city' : self._u2a(p_address.city).upper()[0:35]
223
, 'zip' : self._u2a(p_address.zip).upper()[0:9]
224
, 'country' : p_address.country_id.code or ""
225
, 'reference' : reference
227
s += self._line(l, iban)
231
def _line_count(self, banks):
233
for p_bank, lines in banks.iteritems() :
234
if [l for l in lines if l.amount <= 0.0] :
240
# end def _line_count
242
def generate_edifact(self, cr, uid, ids, context=None) :
243
attachment_obj = self.pool.get('ir.attachment')
244
company = self.pool.get('res.users').browse(cr, uid, uid).company_id
245
currency_name = company.currency_id.name
246
description = 'EDIFACT-file'
247
for order in self.browse(cr, uid, ids) :
248
if order.state == "cancel" : continue
249
att_ids = attachment_obj.search \
251
, [ ('res_model', '=', order._table_name)
252
, ('res_id', '=', order.id)
253
, ('description', '=', description)
257
attachment_obj.unlink(cr, uid, att_ids, context=context)
258
ref = order.reference.replace("/", "").replace(".", "").replace("+", "").replace(":", "").upper()
260
refname = time.strftime("%Y%m%d%H%M%S")
261
company_name = self._u2a(company.name).upper()[0:35]
262
address = self._address(company.partner_id, ['invoice', 'default', False])
263
bank = order.mode.bank_id
264
if not bank.bank.bic :
265
raise osv.except_osv \
267
, _('Bank %s has no BIC') % (bank.bank.name)
270
raise osv.except_osv \
272
, _('Banking Account for bank %s has no IBAN') % (bank.bank.name)
274
iban = bank.iban.replace(" ", "").upper()
275
bic = bank.bank.bic.replace(" ", "").upper()
276
street = self._u2a(address.street).upper()[0:35]
277
city = self._u2a(address.city).upper()[0:35]
278
zip = self._u2a(address.zip).upper()[0:9]
279
amount = "%s" % order.total
280
regions = {'IN' : {}, 'DO' : {}}
281
for line in order.line_ids :
282
if not line.bank_id :
283
raise osv.except_osv \
285
, _('Payment for (%s) needs banking information') % (line.partner_id.name)
287
# if not line.bank_id.bank.bic :
288
# raise osv.except_osv \
289
# ( _('Data Error !')
290
# , _('Bank for (%s) needs BIC') % (line.partner_id.name)
292
if not line.move_line_id :
293
raise osv.except_osv \
295
, _('Payment for (%s) needs accounting information') % (line.partner_id.name)
297
if line.currency != company.currency_id :
298
raise osv.except_osv \
300
, _('Payment contains currency (%s) different from company currency (%s).')
301
% (line.currency.name, currency_name)
303
if ( line.bank_id.bank.country
304
and bank.bank.country
305
and (line.bank_id.bank.country == bank.bank.country)
307
region = "DO" # domestic
309
region = "IN" # international
310
if line.bank_id not in regions[region] :
311
regions[region][line.bank_id] = [line,]
313
regions[region][line.bank_id].append(line)
314
for area_code, banks in regions.iteritems() :
315
for p_bank, lines in banks.iteritems() :
316
if sum(l.amount for l in lines) <= 0.0 :
317
raise osv.except_osv \
319
, _('Payment contains a zero or negative amount for account "%s" of bank "%s".')
320
% (p_bank.acc_number, p_bank.bank.name)
322
for area_code, banks in regions.iteritems() :
323
if len(banks) == 0 : continue
327
s += ("UNB+UNOC:3+%s+%s+%s:%s+%s++PAYMUL'" % (company_name, bic, time.strftime("%y%m%d"), time.strftime("%H%M"),refkey))
328
s += ("UNH+%s+PAYMUL:D:96A:UN:FAT01G'" % refname)
329
s += ("BGM+452+%s+9'" % ref)
330
s += ("DTM+137:%s:203'" % (time.strftime("%Y%m%d%H%M%S")))
331
s += ("FII+MR+%s'" % iban) # sgr2 # xxx
332
s += ("NAD+MS+++%s+%s+%s++%s+%s'" % (company_name, street, city, zip, address.country_id.code)) # sgr3
333
s += ("LIN+1'") # sgr4
334
s += ("DTM+203:%s:102'" % (time.strftime("%Y%m%d")))
335
s += ("RFF+AEK:%s'" % ref)
336
s += ("BUS++%s++TRF'" % area_code)
337
s += ("MOA+9:%s:%s'" % (amount, currency_name))
338
s += ("FII+OR+%s:%s::%s'" % (iban, company_name, currency_name))
339
s += ("NAD+OY+++%s+%s+%s+%s+%s'" % (company_name, street, city, zip, address.country_id.code)) # sgr3
340
s += self._region(order, banks, i)
341
i += self._line_count(banks)
342
s += ("CNT+1:%s'" % amount)
344
s += ("CNT+39:%s'" % i)
345
s += ("UNT+%s+%s'" % (i * 8 + 16, refname))
346
s += ("UNZ+1+%s'" % refkey)
348
{ 'name' : "%s_%s" % (area_code, ref)
349
, 'datas' : base64.encodestring(s)
350
, 'datas_fname' : "%s_%s.txt" % (area_code, ref)
351
, 'res_model' : order._table_name
352
, 'res_id' : order.id
353
, 'description' : description
355
attachment_obj.create(cr, uid, vals, context=context)
356
# end def generate_edifact
357
# end class payment_order