~camptocamp/c2c-rd-addons/8.0a

« back to all changes in this revision

Viewing changes to account_payment_edifact/payment_edifact.py.20110419

  • Committer: ferdinand
  • Date: 2011-04-30 20:34:01 UTC
  • Revision ID: office@chricar.at-20110430203401-eqfv4au4tv3faj93
[ADD] initial

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
##############################################
 
3
#
 
4
# Swing Entwicklung betrieblicher Informationssysteme GmbH
 
5
# (<http://www.swing-system.com>)
 
6
# Copyright (C) ChriCar Beteiligungs- und Beratungs- GmbH
 
7
# all rights reserved
 
8
#    05-AUG-2010 (GK) created
 
9
#
 
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
 
15
# Service Company.
 
16
#
 
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.
 
21
#
 
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.
 
26
#
 
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.
 
31
#
 
32
###############################################
 
33
from osv import fields, osv
 
34
from tools.translate import _
 
35
import os
 
36
import time
 
37
import unicode2ascii
 
38
import base64
 
39
 
 
40
class payment_type(osv.osv) :
 
41
    _inherit = "payment.type"
 
42
    # http://www.unece.org/trade/untdid/d00a/tred/tred4471.htm
 
43
    _columns = \
 
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')
 
51
             , ( '7', 'Optional')
 
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')
 
80
             ]
 
81
            , 'Financial Charges Allocation'
 
82
            )
 
83
        }
 
84
    _defaults = {'charges_alloc'    : lambda *a: '14'}
 
85
# end class payment_type
 
86
payment_type()
 
87
 
 
88
class payment_order(osv.osv) :
 
89
    _inherit = "payment.order"
 
90
 
 
91
    def _address(self, partner, type_list):
 
92
        for type in type_list :
 
93
            for addr in partner.address :
 
94
                if addr.type == type :
 
95
                    return addr
 
96
        raise osv.except_osv \
 
97
            ( _('Data Error !')
 
98
            , _('Partner "%s" has no address defined.')
 
99
                % (partner.name)
 
100
            )
 
101
    # end def _address
 
102
 
 
103
    def _u2a(self, text) :
 
104
        if not text : return ""
 
105
        txt = ""
 
106
        for c in text:
 
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]
 
112
            else : txt+= "_"
 
113
        return txt
 
114
    # end def _u2a
 
115
 
 
116
    def action_open(self, cr, uid, ids, *args):
 
117
        result = True
 
118
        result = super(payment_order, self).action_open(cr, uid, ids, args)
 
119
        self.generate_edifact(cr, uid, ids, {})
 
120
        return result
 
121
    # end def action_open
 
122
    
 
123
    def get_wizard(self, type):
 
124
        return False
 
125
    # end def get_wizard
 
126
 
 
127
    def _line(self, l, iban) :
 
128
        s = ""
 
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)
 
133
        if iban :
 
134
            s += ("FII+BF+%(iban)s:%(name)s+%(bic)s:25:5'" % l)
 
135
        else :
 
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
 
138
        s += ("PRC+11'")
 
139
        if l['reference'] != l['move_name'] :
 
140
            s += ("FTX+PMD+++%(reference)s'" % l)
 
141
        else :
 
142
            s += ("FTX+PMD+++'" % l)
 
143
        return s
 
144
    # end def _line
 
145
    
 
146
    def _region(self, order, banks, i):
 
147
        partner_bank_obj = self.pool.get('res.partner.bank')
 
148
        s = ""
 
149
        for p_bank, lines in banks.iteritems() :
 
150
            if p_bank.state == "iban"  :
 
151
                iban    = p_bank.iban.replace(" ", "").upper()
 
152
                account = False
 
153
            else :
 
154
                iban    = partner_bank_obj._construct_iban(p_bank)
 
155
                account = p_bank.acc_number
 
156
            bic = ''
 
157
            if p_bank.bank.bic:
 
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] :
 
163
                line = lines[0]
 
164
                i += 1
 
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 \
 
170
                    (", ".join
 
171
                        ("%s%s" % 
 
172
                            (invoice.number, "" if not invoice.reference else (" " + invoice.reference)
 
173
                            ) for l in lines
 
174
                        )
 
175
                    )[0:70]
 
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])
 
178
                l = \
 
179
                    { 'seq'       : "%s" % i
 
180
                    , 'amount'    : "%s" % a
 
181
                    , 'bank_name' : bank_name
 
182
                    , 'bank_country' : bank_country
 
183
                    , 'iban'      : iban
 
184
                    , 'account'   : account
 
185
                    , 'bic'       : bic
 
186
                    , 'blz'       : blz
 
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
 
196
                    }
 
197
                s += self._line(l, iban)
 
198
            else :
 
199
                for line in lines :
 
200
                    i += 1
 
201
                    invoice   = line.move_line_id.invoice
 
202
                    reference = self._u2a \
 
203
                        ( line.communication
 
204
                        + ((" " + line.communication2 if line.communication2 else ""))
 
205
                        + ((" " + invoice.reference if invoice.reference else ""))
 
206
                        )[0:70]
 
207
                    p_address = self._address(line.partner_id, ['invoice', 'default', False])
 
208
                    l = \
 
209
                        { 'seq'       : "%s" % i
 
210
                        , 'amount'    : "%s" % line.amount
 
211
                        , 'bank_name' : bank_name
 
212
                        , 'bank_country' : bank_country
 
213
                        , 'iban'      : iban
 
214
                        , 'account'   : account
 
215
                        , 'bic'       : bic
 
216
                        , 'blz'       : blz
 
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
 
226
                        }
 
227
                    s += self._line(l, iban)
 
228
        return s
 
229
    # end def _region
 
230
 
 
231
    def _line_count(self, banks):
 
232
        i = 0
 
233
        for p_bank, lines in banks.iteritems() :
 
234
            if [l for l in lines if l.amount <= 0.0] :
 
235
                i += 1
 
236
            else :
 
237
                for line in lines :
 
238
                    i += 1
 
239
        return i
 
240
    # end def _line_count
 
241
 
 
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 \
 
250
                ( cr, uid
 
251
                , [ ('res_model', '=', order._table_name)
 
252
                  , ('res_id', '=', order.id)
 
253
                  , ('description', '=', description)
 
254
                  ]
 
255
                )
 
256
            if att_ids :
 
257
                attachment_obj.unlink(cr, uid, att_ids, context=context)
 
258
            ref     = order.reference.replace("/", "").replace(".", "").replace("+", "").replace(":", "").upper()
 
259
            refkey  = ref
 
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 \
 
266
                    ( _('Data Error !')
 
267
                    , _('Bank %s has no BIC') % (bank.bank.name)
 
268
                    )
 
269
            if not bank.iban :
 
270
                raise osv.except_osv \
 
271
                    ( _('Data Error !')
 
272
                    , _('Banking Account for bank %s has no IBAN') % (bank.bank.name)
 
273
                    )
 
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 \
 
284
                        ( _('Data Error !')
 
285
                        , _('Payment for (%s) needs banking information') % (line.partner_id.name)
 
286
                        )
 
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)
 
291
#                        )
 
292
                if not line.move_line_id : 
 
293
                    raise osv.except_osv \
 
294
                        ( _('Data Error !')
 
295
                        , _('Payment for (%s) needs accounting information') % (line.partner_id.name)
 
296
                        )
 
297
                if line.currency != company.currency_id : 
 
298
                    raise osv.except_osv \
 
299
                        ( _('Data Error !')
 
300
                        , _('Payment contains currency (%s) different from company currency (%s).')
 
301
                            % (line.currency.name, currency_name)
 
302
                        )
 
303
                if (    line.bank_id.bank.country 
 
304
                    and bank.bank.country
 
305
                    and (line.bank_id.bank.country == bank.bank.country) 
 
306
                   ) :
 
307
                    region = "DO" # domestic
 
308
                else :
 
309
                    region = "IN" # international
 
310
                if line.bank_id not in regions[region] :
 
311
                    regions[region][line.bank_id] = [line,]
 
312
                else :
 
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 \
 
318
                            ( _('Data Error !')
 
319
                            , _('Payment contains a zero or negative amount for account "%s" of bank "%s".')
 
320
                                % (p_bank.acc_number, p_bank.bank.name)
 
321
                            )
 
322
            for area_code, banks in regions.iteritems() :
 
323
                if len(banks) == 0 : continue
 
324
                i = 0  
 
325
                s = ""
 
326
                s += ("UNA:+.? '")
 
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)
 
343
                s += ("CNT+2:1'")
 
344
                s += ("CNT+39:%s'" % i)
 
345
                s += ("UNT+%s+%s'" % (i * 8 + 16, refname))
 
346
                s += ("UNZ+1+%s'" % refkey)
 
347
                vals = \
 
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
 
354
                    }
 
355
                attachment_obj.create(cr, uid, vals, context=context)
 
356
    # end def generate_edifact
 
357
# end class payment_order
 
358
payment_order()