Merge lp:~bellini666/stoqlib/payment_list into lp:~stoq-dev/stoqlib/master

Proposed by Thiago Bellini
Status: Superseded
Proposed branch: lp:~bellini666/stoqlib/payment_list
Merge into: lp:~stoq-dev/stoqlib/master
Diff against target: 2704 lines (+1065/-1154)
16 files modified
data/glade/BankDataSlave.glade (+80/-62)
data/glade/BasePaymentDataEditor.glade (+118/-0)
data/glade/BasePaymentMethodSlave.glade (+162/-253)
data/glade/BillDataSlave.glade (+0/-124)
data/glade/PaymentListSlave.glade (+48/-56)
data/glade/PurchasePaymentStep.glade (+1/-66)
stoqlib/database/admin.py (+1/-1)
stoqlib/database/testsuite.py (+1/-1)
stoqlib/domain/payment/method.py (+6/-3)
stoqlib/domain/payment/operation.py (+1/-1)
stoqlib/domain/payment/payment.py (+3/-1)
stoqlib/gui/slaves/paymentslave.py (+431/-553)
stoqlib/gui/wizards/purchasewizard.py (+4/-10)
stoqlib/lib/defaults.py (+18/-23)
stoqlib/lib/payment.py (+61/-0)
stoqlib/lib/test/test_payment.py (+130/-0)
To merge this branch: bzr merge lp:~bellini666/stoqlib/payment_list
Reviewer Review Type Date Requested Status
Ronaldo Maia Needs Fixing
Review via email: mp+48035@code.launchpad.net

This proposal has been superseded by a proposal from 2011-02-01.

To post a comment you must log in.
Revision history for this message
Ronaldo Maia (romaia) wrote :

Alguns ultimos comentários:

(MoneyMethodSlave|StoreCreditMethodSlave).create_model fazem a mesma coisa que o BasePaymentMethodSlave.create_model. Eles não precisam mais ser declarados.

     def after_installments_number__changed(self, *args):
+ has_installments = self.model.installments_number > 1

Espaço em branco a mais depois do =

=== added file 'stoqlib/lib/test/test_payment.py'
+## Copyright (C) 2008 Async Open Source <http://www.async.com.br>
+## All rights reserved

Copyright 2011

Não gostei muito como os testes ficaram. Achei eles meio difíceis de ler.

Algo assim fica mais legível:

   first_due_date = datetime.date(year=2010, month=4, day=1)
   due_dates = generate_payments_due_dates(5, first_due_date, 1, INTERVALTYPE_MONTH)
   expected_due_dates = [datetime.date(2010, 4, 1), ..., datetime.date(2010, 8, 1)]

   self.assertEqual(due_dates, expected_due_dates)

Tem outros aspectos que vc pode testar também. Como:

   self.assertEqual(len(due_dates), 5)

E nos testes, vc só adicionou para mês e semana, faltou também para dias e anos.

A mesma coisa para o teste dos valores.

Vc pode testar também: se a quantidade está certa, a soma dos valores deves ser igual ao total original informado, no caso de informar os juros, tem que verificar se o calculo está certo também.

Corrige esses detalhes e antes de fazer esse último commit, me chama para eu ver como ficaram os testes. Que vou explicar como deve ser feito esse merge.

review: Needs Fixing
lp:~bellini666/stoqlib/payment_list updated
3304. By Thiago Bellini

Refactoring of PaymentListSlave class and bugfixes.

3305. By Ronaldo Maia

Adicionando parametro para exibir (ou não) a coluna custo no wizard de venda (customização)

3306. By Johan Dahlin

Remove individual authors

Most people do no longer work on the project, so there's no use in
displaying them in the copyright header. Besides we're using a
versioning control system which can be used if anyone really want to know
who wrote the code.

3307. By Ronaldo Maia

Removendo restante do ValidatableDomain

3308. By Ronaldo Maia

clean up after removing individual authors

3309. By Ronaldo Maia

update translation files

3310. By Ronaldo Maia

copy tranlations file from first attempt to use launchpad translations

3311. By Ronaldo Maia

Release 0.9.15

3312. By Thiago Bellini

Add an option to change the freight type when receiving an order. Fixes #4245

3313. By Gabriel Gerga

Removed retention functionality and added cfop field in stock_decrease_item table. r=romaia

3314. By Gabriel Gerga

Create StockDecreaseItemEditor, inheriting from PurchaseItemEditor. r=romaia

3315. By George Kussumoto

Fix: supports_duplicate_receipt is a property, not a method.

3316. By Thiago Bellini

Modifications to Purchase Details dialog to make it more useful. Fixes #4245

3317. By Gabriel Gerga

Added print dialog when confirmed a quote. r=romaia

3318. By Thiago Bellini

When editing a purchase, generate a better PaymentList.

When editing, the PaymentList will create the payments based on the number of
installments it had before and the first due date will be set to the first's
payment due date.

3319. By Thiago Bellini

When editing a row on PaymentList, set focus on payment number.

3320. By Thiago Bellini

Code corrections.

Unmerged revisions

3320. By Thiago Bellini

Code corrections.

3319. By Thiago Bellini

When editing a row on PaymentList, set focus on payment number.

3318. By Thiago Bellini

When editing a purchase, generate a better PaymentList.

When editing, the PaymentList will create the payments based on the number of
installments it had before and the first due date will be set to the first's
payment due date.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/glade/BankDataSlave.glade'
2--- data/glade/BankDataSlave.glade 2006-06-08 19:02:06 +0000
3+++ data/glade/BankDataSlave.glade 2011-02-01 20:17:04 +0000
4@@ -4,88 +4,106 @@
5 <widget class="GtkWindow" id="BankDataSlave">
6 <property name="default_height">250</property>
7 <property name="default_width">440</property>
8+ <property name="mnemonics_visible">False</property>
9 <child>
10 <widget class="GtkVBox" id="vbox1">
11+ <property name="border_width">3</property>
12+ <property name="spacing">5</property>
13 <property name="visible">True</property>
14 <child>
15 <widget class="GtkHSeparator" id="hseparator1">
16 <property name="visible">True</property>
17 </widget>
18- <packing>
19- <property name="expand">False</property>
20- <property name="padding">6</property>
21- </packing>
22 </child>
23 <child>
24- <widget class="GtkHBox" id="hbox1">
25- <property name="spacing">5</property>
26+ <widget class="GtkTable" id="table1">
27+ <property name="column_spacing">6</property>
28+ <property name="n_columns">2</property>
29+ <property name="n_rows">3</property>
30+ <property name="row_spacing">6</property>
31 <property name="visible">True</property>
32 <child>
33- <widget class="GtkAlignment" id="alignment1">
34- <property name="visible">True</property>
35- <child>
36- <widget class="kiwi+ui+widgets+label+Label" id="kiwilabel1">
37- <property name="data_type">unicode</property>
38- <property name="label" context="yes" translatable="yes">Bank ID:</property>
39- <property name="visible">True</property>
40- <property name="xalign">1.0</property>
41- </widget>
42- </child>
43- </widget>
44- <packing>
45- <property name="expand">False</property>
46- </packing>
47- </child>
48- <child>
49- <widget class="kiwi+ui+widgets+entry+Entry" id="bank">
50+ <widget class="ProxyLabel" id="kiwilabel1">
51+ <property name="data_type">unicode</property>
52+ <property name="label" context="yes" translatable="yes">Bank ID:</property>
53+ <property name="model_attribute">kiwilabel1</property>
54+ <property name="visible">True</property>
55+ <property name="xalign">1.0</property>
56+ </widget>
57+ </child>
58+ <child>
59+ <widget class="ProxyLabel" id="kiwilabel2">
60+ <property name="data_type">unicode</property>
61+ <property name="label" context="yes" translatable="yes">Bank Branch:</property>
62+ <property name="model_attribute">kiwilabel2</property>
63+ <property name="visible">True</property>
64+ <property name="xalign">1.0</property>
65+ </widget>
66+ <packing>
67+ <property name="bottom_attach">2</property>
68+ <property name="top_attach">1</property>
69+ </packing>
70+ </child>
71+ <child>
72+ <widget class="ProxyLabel" id="kiwilabel3">
73+ <property name="label" context="yes" translatable="yes">Bank Account:</property>
74+ <property name="model_attribute">kiwilabel3</property>
75+ <property name="visible">True</property>
76+ <property name="xalign">1.0</property>
77+ </widget>
78+ <packing>
79+ <property name="bottom_attach">3</property>
80+ <property name="top_attach">2</property>
81+ </packing>
82+ </child>
83+ <child>
84+ <widget class="ProxyEntry" id="bank_id">
85 <property name="data_type">int</property>
86 <property name="is_focus">True</property>
87 <property name="model_attribute">bank_id</property>
88 <property name="visible">True</property>
89 <property name="width_chars">10</property>
90- </widget>
91- <packing>
92- <property name="expand">False</property>
93- <property name="position">1</property>
94- </packing>
95- </child>
96- <child>
97- <widget class="kiwi+ui+widgets+label+Label" id="kiwilabel2">
98- <property name="data_type">unicode</property>
99- <property name="label" context="yes" translatable="yes">Branch Number:</property>
100- <property name="visible">True</property>
101- <property name="xalign">1.0</property>
102- </widget>
103- <packing>
104- <property name="expand">False</property>
105- <property name="position">2</property>
106- </packing>
107- </child>
108- <child>
109- <widget class="kiwi+ui+widgets+entry+Entry" id="branch">
110- <property name="data_type">unicode</property>
111- <property name="is_focus">True</property>
112- <property name="model_attribute">branch</property>
113- <property name="visible">True</property>
114- <property name="width_chars">10</property>
115- </widget>
116- <packing>
117- <property name="expand">False</property>
118- <property name="position">3</property>
119- </packing>
120- </child>
121- <child>
122- <widget class="GtkLabel" id="label1">
123- <property name="visible">True</property>
124- </widget>
125- <packing>
126- <property name="expand">False</property>
127- <property name="position">4</property>
128+ <property name="xalign">1.0</property>
129+ </widget>
130+ <packing>
131+ <property name="left_attach">1</property>
132+ <property name="right_attach">2</property>
133+ </packing>
134+ </child>
135+ <child>
136+ <widget class="ProxyEntry" id="bank_branch">
137+ <property name="data_type">str</property>
138+ <property name="is_focus">True</property>
139+ <property name="model_attribute">bank_branch</property>
140+ <property name="visible">True</property>
141+ <property name="width_chars">10</property>
142+ <property name="xalign">1.0</property>
143+ </widget>
144+ <packing>
145+ <property name="bottom_attach">2</property>
146+ <property name="left_attach">1</property>
147+ <property name="right_attach">2</property>
148+ <property name="top_attach">1</property>
149+ </packing>
150+ </child>
151+ <child>
152+ <widget class="ProxyEntry" id="bank_account">
153+ <property name="data_type">str</property>
154+ <property name="is_focus">True</property>
155+ <property name="model_attribute">bank_account</property>
156+ <property name="visible">True</property>
157+ <property name="width_chars">10</property>
158+ <property name="xalign">1.0</property>
159+ </widget>
160+ <packing>
161+ <property name="bottom_attach">3</property>
162+ <property name="left_attach">1</property>
163+ <property name="right_attach">2</property>
164+ <property name="top_attach">2</property>
165 </packing>
166 </child>
167 </widget>
168 <packing>
169- <property name="expand">False</property>
170 <property name="position">1</property>
171 </packing>
172 </child>
173
174=== added file 'data/glade/BasePaymentDataEditor.glade'
175--- data/glade/BasePaymentDataEditor.glade 1970-01-01 00:00:00 +0000
176+++ data/glade/BasePaymentDataEditor.glade 2011-02-01 20:17:04 +0000
177@@ -0,0 +1,118 @@
178+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
179+<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
180+<glade-interface domain="stoqlib">
181+ <widget class="GtkWindow" id="BasePaymentDataEditor">
182+ <property name="border_width">5</property>
183+ <property name="default_height">500</property>
184+ <property name="default_width">500</property>
185+ <property name="mnemonics_visible">False</property>
186+ <child>
187+ <widget class="GtkVBox" id="vbox1">
188+ <property name="border_width">5</property>
189+ <property name="spacing">3</property>
190+ <property name="visible">True</property>
191+ <child>
192+ <widget class="GtkTable" id="table1">
193+ <property name="column_spacing">6</property>
194+ <property name="n_columns">2</property>
195+ <property name="n_rows">3</property>
196+ <property name="row_spacing">6</property>
197+ <property name="visible">True</property>
198+ <child>
199+ <widget class="ProxyLabel" id="kiwilabel1">
200+ <property name="data_type">unicode</property>
201+ <property name="label" context="yes" translatable="yes">Due date:</property>
202+ <property name="model_attribute">kiwilabel1</property>
203+ <property name="visible">True</property>
204+ <property name="xalign">1.0</property>
205+ </widget>
206+ </child>
207+ <child>
208+ <widget class="ProxyLabel" id="kiwilabel2">
209+ <property name="data_type">unicode</property>
210+ <property name="label" context="yes" translatable="yes">Value ($CURRENCY):</property>
211+ <property name="model_attribute">kiwilabel2</property>
212+ <property name="visible">True</property>
213+ <property name="xalign">1.0</property>
214+ </widget>
215+ <packing>
216+ <property name="bottom_attach">2</property>
217+ <property name="top_attach">1</property>
218+ </packing>
219+ </child>
220+ <child>
221+ <widget class="ProxyLabel" id="kiwilabel3">
222+ <property name="data_type">unicode</property>
223+ <property name="label" context="yes" translatable="yes">Number:</property>
224+ <property name="model_attribute">kiwilabel3</property>
225+ <property name="visible">True</property>
226+ <property name="xalign">1.0</property>
227+ </widget>
228+ <packing>
229+ <property name="bottom_attach">3</property>
230+ <property name="top_attach">2</property>
231+ </packing>
232+ </child>
233+ <child>
234+ <widget class="ProxyDateEntry" id="due_date">
235+ <property name="data_type">date</property>
236+ <property name="model_attribute">due_date</property>
237+ <property name="visible">True</property>
238+ </widget>
239+ <packing>
240+ <property name="left_attach">1</property>
241+ <property name="right_attach">2</property>
242+ </packing>
243+ </child>
244+ <child>
245+ <widget class="ProxyEntry" id="value">
246+ <property name="data_type">Decimal</property>
247+ <property name="is_focus">True</property>
248+ <property name="mandatory">True</property>
249+ <property name="model_attribute">value</property>
250+ <property name="visible">True</property>
251+ <property name="width_chars">10</property>
252+ <property name="width_request">90</property>
253+ <property name="xalign">1.0</property>
254+ </widget>
255+ <packing>
256+ <property name="bottom_attach">2</property>
257+ <property name="left_attach">1</property>
258+ <property name="right_attach">2</property>
259+ <property name="top_attach">1</property>
260+ </packing>
261+ </child>
262+ <child>
263+ <widget class="ProxyEntry" id="payment_number">
264+ <property name="data_type">str</property>
265+ <property name="is_focus">True</property>
266+ <property name="model_attribute">payment_number</property>
267+ <property name="visible">True</property>
268+ <property name="width_chars">10</property>
269+ <property name="width_request">90</property>
270+ <property name="xalign">1.0</property>
271+ </widget>
272+ <packing>
273+ <property name="bottom_attach">3</property>
274+ <property name="left_attach">1</property>
275+ <property name="right_attach">2</property>
276+ <property name="top_attach">2</property>
277+ </packing>
278+ </child>
279+ </widget>
280+ </child>
281+ <child>
282+ <widget class="GtkEventBox" id="bank_data_slave">
283+ <property name="visible">True</property>
284+ <child>
285+ <placeholder/>
286+ </child>
287+ </widget>
288+ <packing>
289+ <property name="position">1</property>
290+ </packing>
291+ </child>
292+ </widget>
293+ </child>
294+ </widget>
295+</glade-interface>
296
297=== renamed file 'data/glade/BillCheckMethodSlave.glade' => 'data/glade/BasePaymentMethodSlave.glade'
298--- data/glade/BillCheckMethodSlave.glade 2007-11-01 14:23:26 +0000
299+++ data/glade/BasePaymentMethodSlave.glade 2011-02-01 20:17:04 +0000
300@@ -1,9 +1,10 @@
301 <?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
302 <!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
303 <glade-interface domain="stoqlib">
304- <widget class="GtkWindow" id="BillCheckMethodSlave">
305- <property name="default_height">250</property>
306- <property name="default_width">440</property>
307+ <widget class="GtkWindow" id="BasePaymentMethodSlave">
308+ <property name="default_height">450</property>
309+ <property name="default_width">250</property>
310+ <property name="mnemonics_visible">False</property>
311 <child>
312 <widget class="GtkFrame" id="frame1">
313 <property name="visible">True</property>
314@@ -12,64 +13,26 @@
315 <property name="visible">True</property>
316 <child>
317 <widget class="GtkTable" id="table1">
318- <property name="border_width">5</property>
319- <property name="column_spacing">5</property>
320- <property name="n_columns">5</property>
321- <property name="n_rows">4</property>
322- <property name="row_spacing">5</property>
323+ <property name="border_width">3</property>
324+ <property name="column_spacing">6</property>
325+ <property name="n_columns">3</property>
326+ <property name="n_rows">2</property>
327+ <property name="row_spacing">6</property>
328 <property name="visible">True</property>
329 <child>
330- <widget class="ProxyLabel" id="kiwilabel1">
331- <property name="data_type">unicode</property>
332- <property name="label" context="yes" translatable="yes">Number of installments:</property>
333- <property name="model_attribute">kiwilabel1</property>
334- <property name="visible">True</property>
335- <property name="xalign">1.0</property>
336- </widget>
337- <packing>
338- <property name="y_options">fill</property>
339- </packing>
340- </child>
341- <child>
342- <widget class="ProxyLabel" id="first_duedate_lbl">
343- <property name="data_type">unicode</property>
344- <property name="label" context="yes" translatable="yes">First installment due:</property>
345- <property name="model_attribute">first_duedate_lbl</property>
346- <property name="visible">True</property>
347- <property name="xalign">1.0</property>
348- </widget>
349- <packing>
350- <property name="bottom_attach">2</property>
351- <property name="top_attach">1</property>
352- </packing>
353- </child>
354- <child>
355- <widget class="ProxyLabel" id="kiwilabel5">
356- <property name="data_type">unicode</property>
357- <property name="label" context="yes" translatable="yes">Based on intervals of:</property>
358- <property name="model_attribute">kiwilabel5</property>
359- <property name="visible">True</property>
360- <property name="xalign">1.0</property>
361- </widget>
362- <packing>
363- <property name="bottom_attach">3</property>
364- <property name="top_attach">2</property>
365- </packing>
366- </child>
367- <child>
368- <widget class="GtkLabel" id="bank_label">
369- <property name="label" context="yes" translatable="yes">Default Bank:</property>
370- <property name="visible">True</property>
371- <property name="xalign">1.0</property>
372- </widget>
373- <packing>
374- <property name="bottom_attach">4</property>
375- <property name="top_attach">3</property>
376- </packing>
377- </child>
378- <child>
379- <widget class="GtkHBox" id="hbox2">
380- <property name="visible">True</property>
381+ <widget class="GtkHBox" id="hbox1">
382+ <property name="spacing">6</property>
383+ <property name="visible">True</property>
384+ <child>
385+ <widget class="ProxyLabel" id="installments_number_lbl">
386+ <property name="data_type">unicode</property>
387+ <property name="justify">right</property>
388+ <property name="label" context="yes" translatable="yes">Installments:</property>
389+ <property name="model_attribute">installments_number_lbl</property>
390+ <property name="visible">True</property>
391+ <property name="xalign">1.0</property>
392+ </widget>
393+ </child>
394 <child>
395 <widget class="ProxySpinButton" id="installments_number">
396 <property name="adjustment">0 0 100 1 10 0</property>
397@@ -78,199 +41,145 @@
398 <property name="model_attribute">installments_number</property>
399 <property name="text" context="yes" translatable="yes">0</property>
400 <property name="visible">True</property>
401- <property name="width_chars">6</property>
402- <property name="width_request">90</property>
403- </widget>
404- <packing>
405- <property name="expand">False</property>
406- </packing>
407- </child>
408- <child>
409- <placeholder/>
410- <packing>
411- <property name="position">1</property>
412- </packing>
413- </child>
414- </widget>
415- <packing>
416- <property name="left_attach">1</property>
417- <property name="right_attach">2</property>
418- <property name="x_options">fill</property>
419- <property name="y_options">fill</property>
420- </packing>
421- </child>
422- <child>
423- <widget class="ProxyDateEntry" id="first_duedate">
424- <property name="data_type">date</property>
425- <property name="model_attribute">first_duedate</property>
426- <property name="visible">True</property>
427- </widget>
428- <packing>
429- <property name="bottom_attach">2</property>
430- <property name="left_attach">1</property>
431- <property name="right_attach">2</property>
432- <property name="top_attach">1</property>
433- <property name="x_options">fill</property>
434- </packing>
435- </child>
436- <child>
437- <widget class="GtkHBox" id="hbox3">
438- <property name="visible">True</property>
439- <child>
440- <widget class="ProxySpinButton" id="intervals">
441- <property name="adjustment">0 0 100 1 10 0</property>
442- <property name="data_type">int</property>
443- <property name="is_focus">True</property>
444- <property name="model_attribute">intervals</property>
445- <property name="text" context="yes" translatable="yes">0</property>
446- <property name="visible">True</property>
447- <property name="width_chars">6</property>
448- <property name="width_request">90</property>
449- </widget>
450- <packing>
451- <property name="expand">False</property>
452- </packing>
453- </child>
454- <child>
455- <placeholder/>
456- <packing>
457- <property name="position">1</property>
458- </packing>
459- </child>
460- <child>
461- <placeholder/>
462- <packing>
463- <property name="position">2</property>
464- </packing>
465- </child>
466- </widget>
467- <packing>
468- <property name="bottom_attach">3</property>
469- <property name="left_attach">1</property>
470- <property name="right_attach">2</property>
471- <property name="top_attach">2</property>
472- <property name="x_options">fill</property>
473- </packing>
474- </child>
475- <child>
476- <widget class="ProxyComboEntry" id="bank_combo">
477- <property name="data_type">str</property>
478- <property name="model_attribute"></property>
479- <property name="visible">True</property>
480- </widget>
481- <packing>
482- <property name="bottom_attach">4</property>
483- <property name="left_attach">1</property>
484- <property name="right_attach">4</property>
485- <property name="top_attach">3</property>
486- </packing>
487- </child>
488- <child>
489- <placeholder/>
490- <packing>
491- <property name="left_attach">2</property>
492- <property name="right_attach">3</property>
493- <property name="x_options">fill</property>
494- <property name="y_options">fill</property>
495- </packing>
496- </child>
497- <child>
498- <placeholder/>
499- <packing>
500- <property name="left_attach">2</property>
501- <property name="right_attach">3</property>
502- <property name="top_attach">1</property>
503- <property name="bottom_attach">2</property>
504- </packing>
505- </child>
506- <child>
507- <widget class="GtkHBox" id="hbox1">
508- <property name="visible">True</property>
509- <property name="width_request">90</property>
510- <child>
511- <widget class="ProxyComboBox" id="interval_type_combo">
512- <property name="data_type">int</property>
513- <property name="model_attribute">interval_type</property>
514- <property name="visible">True</property>
515- </widget>
516- </child>
517- </widget>
518- <packing>
519- <property name="bottom_attach">3</property>
520- <property name="left_attach">2</property>
521- <property name="right_attach">3</property>
522- <property name="top_attach">2</property>
523- <property name="x_options">fill</property>
524- <property name="y_options">expand | shrink</property>
525- </packing>
526- </child>
527- <child>
528- <placeholder/>
529- <packing>
530- <property name="left_attach">3</property>
531- <property name="right_attach">4</property>
532- <property name="x_options">fill</property>
533- </packing>
534- </child>
535- <child>
536- <placeholder/>
537- <packing>
538- <property name="left_attach">3</property>
539- <property name="right_attach">4</property>
540- <property name="top_attach">1</property>
541- <property name="bottom_attach">2</property>
542- </packing>
543- </child>
544- <child>
545- <widget class="GtkButton" id="reset_button">
546- <property name="is_focus">True</property>
547- <property name="label" context="yes" translatable="yes">_Reset List</property>
548- <property name="use_underline">True</property>
549- <property name="visible">True</property>
550- </widget>
551- <packing>
552- <property name="bottom_attach">3</property>
553- <property name="left_attach">3</property>
554- <property name="right_attach">4</property>
555- <property name="top_attach">2</property>
556- <property name="x_options">fill</property>
557- </packing>
558- </child>
559- <child>
560- <placeholder/>
561- <packing>
562- <property name="left_attach">4</property>
563- <property name="right_attach">5</property>
564- </packing>
565- </child>
566- <child>
567- <placeholder/>
568- <packing>
569- <property name="left_attach">4</property>
570- <property name="right_attach">5</property>
571- <property name="top_attach">1</property>
572- <property name="bottom_attach">2</property>
573- </packing>
574- </child>
575- <child>
576- <widget class="GtkLabel" id="label1">
577- <property name="visible">True</property>
578- </widget>
579- <packing>
580- <property name="bottom_attach">3</property>
581- <property name="left_attach">4</property>
582- <property name="right_attach">5</property>
583- <property name="top_attach">2</property>
584- <property name="y_options">fill</property>
585- </packing>
586- </child>
587- <child>
588- <placeholder/>
589- <packing>
590- <property name="left_attach">4</property>
591- <property name="right_attach">5</property>
592- <property name="top_attach">3</property>
593- <property name="bottom_attach">4</property>
594+ <property name="width_chars">3</property>
595+ <property name="width_request">90</property>
596+ </widget>
597+ <packing>
598+ <property name="position">1</property>
599+ </packing>
600+ </child>
601+ </widget>
602+ </child>
603+ <child>
604+ <widget class="GtkHBox" id="hbox4">
605+ <property name="spacing">6</property>
606+ <property name="visible">True</property>
607+ <child>
608+ <widget class="GtkLabel" id="bank_label">
609+ <property name="justify">right</property>
610+ <property name="label" context="yes" translatable="yes">Default Bank:</property>
611+ <property name="visible">True</property>
612+ <property name="xalign">1.0</property>
613+ </widget>
614+ </child>
615+ <child>
616+ <widget class="ProxyComboEntry" id="bank_combo">
617+ <property name="data_type">str</property>
618+ <property name="model_attribute">bank_combo</property>
619+ <property name="visible">True</property>
620+ </widget>
621+ <packing>
622+ <property name="position">1</property>
623+ </packing>
624+ </child>
625+ </widget>
626+ <packing>
627+ <property name="bottom_attach">2</property>
628+ <property name="right_attach">2</property>
629+ <property name="top_attach">1</property>
630+ <property name="x_padding">4</property>
631+ </packing>
632+ </child>
633+ <child>
634+ <widget class="ProxyLabel" id="kiwilabel2">
635+ <property name="model_attribute">kiwilabel2</property>
636+ <property name="visible">True</property>
637+ <property name="width_chars">10</property>
638+ <property name="xalign">0.0</property>
639+ </widget>
640+ <packing>
641+ <property name="left_attach">1</property>
642+ <property name="right_attach">2</property>
643+ </packing>
644+ </child>
645+ <child>
646+ <widget class="GtkHBox" id="hbox5">
647+ <property name="spacing">6</property>
648+ <property name="visible">True</property>
649+ <child>
650+ <widget class="GtkHBox" id="hbox2">
651+ <property name="spacing">6</property>
652+ <property name="visible">True</property>
653+ <child>
654+ <widget class="ProxyLabel" id="first_duedate_lbl">
655+ <property name="data_type">unicode</property>
656+ <property name="justify">right</property>
657+ <property name="label" context="yes" translatable="yes">Start at:</property>
658+ <property name="model_attribute">first_duedate_lbl</property>
659+ <property name="visible">True</property>
660+ <property name="xalign">1.0</property>
661+ </widget>
662+ </child>
663+ <child>
664+ <widget class="ProxyDateEntry" id="first_duedate">
665+ <property name="data_type">date</property>
666+ <property name="model_attribute">first_duedate</property>
667+ <property name="visible">True</property>
668+ </widget>
669+ <packing>
670+ <property name="position">1</property>
671+ </packing>
672+ </child>
673+ </widget>
674+ </child>
675+ <child>
676+ <widget class="GtkHBox" id="hbox3">
677+ <property name="spacing">6</property>
678+ <property name="visible">True</property>
679+ <child>
680+ <widget class="ProxyLabel" id="intervals_lbl">
681+ <property name="data_type">unicode</property>
682+ <property name="justify">right</property>
683+ <property name="label" context="yes" translatable="yes">With intervals of:</property>
684+ <property name="model_attribute">intervals_lbl</property>
685+ <property name="visible">True</property>
686+ <property name="xalign">1.0</property>
687+ </widget>
688+ </child>
689+ <child>
690+ <widget class="ProxySpinButton" id="intervals">
691+ <property name="adjustment">0 0 100 1 10 0</property>
692+ <property name="data_type">int</property>
693+ <property name="is_focus">True</property>
694+ <property name="model_attribute">intervals</property>
695+ <property name="text" context="yes" translatable="yes">0</property>
696+ <property name="visible">True</property>
697+ <property name="width_chars">3</property>
698+ <property name="width_request">90</property>
699+ </widget>
700+ <packing>
701+ <property name="position">1</property>
702+ </packing>
703+ </child>
704+ <child>
705+ <widget class="ProxyComboBox" id="interval_type_combo">
706+ <property name="data_type">int</property>
707+ <property name="model_attribute">interval_type</property>
708+ <property name="visible">True</property>
709+ </widget>
710+ <packing>
711+ <property name="padding">2</property>
712+ <property name="position">2</property>
713+ </packing>
714+ </child>
715+ </widget>
716+ <packing>
717+ <property name="position">1</property>
718+ </packing>
719+ </child>
720+ </widget>
721+ <packing>
722+ <property name="left_attach">2</property>
723+ <property name="right_attach">3</property>
724+ </packing>
725+ </child>
726+ <child>
727+ <placeholder/>
728+ <packing>
729+ <property name="left_attach">2</property>
730+ <property name="right_attach">3</property>
731+ <property name="top_attach">1</property>
732+ <property name="bottom_attach">2</property>
733 </packing>
734 </child>
735 </widget>
736@@ -279,7 +188,7 @@
737 </packing>
738 </child>
739 <child>
740- <widget class="GtkEventBox" id="bill_check_data_list">
741+ <widget class="GtkEventBox" id="slave_holder">
742 <property name="visible">True</property>
743 <child>
744 <placeholder/>
745
746=== removed file 'data/glade/BillDataSlave.glade'
747--- data/glade/BillDataSlave.glade 2011-01-17 13:41:17 +0000
748+++ data/glade/BillDataSlave.glade 1970-01-01 00:00:00 +0000
749@@ -1,124 +0,0 @@
750-<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
751-<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
752-<glade-interface domain="stoqlib">
753- <widget class="GtkWindow" id="BillDataSlave">
754- <property name="default_height">250</property>
755- <property name="default_width">440</property>
756- <child>
757- <widget class="GtkVBox" id="vbox1">
758- <property name="border_width">5</property>
759- <property name="visible">True</property>
760- <child>
761- <widget class="kiwi+ui+widgets+label+Label" id="payment_number_label">
762- <property name="data_type">unicode</property>
763- <property name="visible">True</property>
764- <property name="xalign">0.0</property>
765- </widget>
766- <packing>
767- <property name="expand">False</property>
768- </packing>
769- </child>
770- <child>
771- <widget class="GtkVBox" id="vbox2">
772- <property name="visible">True</property>
773- <child>
774- <widget class="GtkHBox" id="hbox1">
775- <property name="border_width">3</property>
776- <property name="spacing">5</property>
777- <property name="visible">True</property>
778- <child>
779- <widget class="kiwi+ui+widgets+label+Label" id="kiwilabel1">
780- <property name="data_type">unicode</property>
781- <property name="label" context="yes" translatable="yes">Duedate:</property>
782- <property name="visible">True</property>
783- <property name="xalign">1.0</property>
784- </widget>
785- <packing>
786- <property name="expand">False</property>
787- </packing>
788- </child>
789- <child>
790- <widget class="ProxyDateEntry" id="due_date">
791- <property name="data_type">date</property>
792- <property name="model_attribute">due_date</property>
793- <property name="visible">True</property>
794- </widget>
795- <packing>
796- <property name="position">1</property>
797- </packing>
798- </child>
799- <child>
800- <widget class="ProxyLabel" id="kiwilabel2">
801- <property name="data_type">unicode</property>
802- <property name="label" context="yes" translatable="yes">Value ($CURRENCY):</property>
803- <property name="visible">True</property>
804- <property name="xalign">1.0</property>
805- </widget>
806- <packing>
807- <property name="expand">False</property>
808- <property name="position">2</property>
809- </packing>
810- </child>
811- <child>
812- <widget class="kiwi+ui+widgets+entry+Entry" id="value">
813- <property name="data_type">currency</property>
814- <property name="is_focus">True</property>
815- <property name="mandatory">True</property>
816- <property name="model_attribute">value</property>
817- <property name="visible">True</property>
818- <property name="width_request">90</property>
819- <property name="xalign">1.0</property>
820- </widget>
821- <packing>
822- <property name="position">3</property>
823- </packing>
824- </child>
825- <child>
826- <widget class="kiwi+ui+widgets+label+Label" id="kiwilabel3">
827- <property name="data_type">unicode</property>
828- <property name="label" context="yes" translatable="yes">Number:</property>
829- <property name="visible">True</property>
830- <property name="xalign">1.0</property>
831- </widget>
832- <packing>
833- <property name="expand">False</property>
834- <property name="position">4</property>
835- </packing>
836- </child>
837- <child>
838- <widget class="kiwi+ui+widgets+entry+Entry" id="payment_number">
839- <property name="data_type">unicode</property>
840- <property name="is_focus">True</property>
841- <property name="model_attribute">payment_number</property>
842- <property name="visible">True</property>
843- <property name="width_request">90</property>
844- </widget>
845- <packing>
846- <property name="position">5</property>
847- </packing>
848- </child>
849- </widget>
850- <packing>
851- <property name="expand">False</property>
852- </packing>
853- </child>
854- <child>
855- <widget class="GtkEventBox" id="bank_data_slave">
856- <property name="visible">True</property>
857- <child>
858- <placeholder/>
859- </child>
860- </widget>
861- <packing>
862- <property name="position">1</property>
863- </packing>
864- </child>
865- </widget>
866- <packing>
867- <property name="position">1</property>
868- </packing>
869- </child>
870- </widget>
871- </child>
872- </widget>
873-</glade-interface>
874
875=== modified file 'data/glade/PaymentListSlave.glade'
876--- data/glade/PaymentListSlave.glade 2006-06-08 19:02:06 +0000
877+++ data/glade/PaymentListSlave.glade 2011-02-01 20:17:04 +0000
878@@ -4,28 +4,17 @@
879 <widget class="GtkWindow" id="PaymentListSlave">
880 <property name="default_height">250</property>
881 <property name="default_width">440</property>
882+ <property name="mnemonics_visible">False</property>
883 <child>
884 <widget class="GtkVBox" id="vbox1">
885 <property name="border_width">5</property>
886 <property name="spacing">5</property>
887 <property name="visible">True</property>
888 <child>
889- <widget class="GtkScrolledWindow" id="scrolled_window">
890- <property name="hscrollbar_policy">never</property>
891+ <widget class="ObjectList" id="payment_list">
892+ <property name="border_width">5</property>
893+ <property name="selection_mode">single</property>
894 <property name="visible">True</property>
895- <child>
896- <widget class="GtkViewport" id="viewport4">
897- <property name="visible">True</property>
898- <child>
899- <widget class="GtkVBox" id="list_vbox">
900- <property name="visible">True</property>
901- <child>
902- <placeholder/>
903- </child>
904- </widget>
905- </child>
906- </widget>
907- </child>
908 </widget>
909 </child>
910 <child>
911@@ -34,64 +23,67 @@
912 <property name="spacing">5</property>
913 <property name="visible">True</property>
914 <child>
915- <widget class="GtkButton" id="add_button">
916- <property name="is_focus">True</property>
917- <property name="visible">True</property>
918- <child>
919- <widget class="GtkImage" id="image1">
920- <property name="stock">gtk-add</property>
921- <property name="visible">True</property>
922- </widget>
923- </child>
924- </widget>
925- <packing>
926- <property name="expand">False</property>
927- </packing>
928- </child>
929- <child>
930- <widget class="GtkButton" id="remove_button">
931- <property name="is_focus">True</property>
932- <property name="visible">True</property>
933- <child>
934- <widget class="GtkImage" id="image2">
935- <property name="stock">gtk-remove</property>
936- <property name="visible">True</property>
937- </widget>
938- </child>
939- </widget>
940- <packing>
941- <property name="expand">False</property>
942- <property name="position">1</property>
943- </packing>
944- </child>
945- <child>
946- <widget class="kiwi+ui+widgets+label+Label" id="status_label">
947- <property name="data_type">unicode</property>
948+ <widget class="ProxyLabel" id="difference_status_label">
949+ <property name="data_type">str</property>
950 <property name="label" context="yes" translatable="yes">Outstanding:</property>
951+ <property name="model_attribute">difference_status_label</property>
952+ <property name="visible">True</property>
953 <property name="xalign">1.0</property>
954- <property name="visible">True</property>
955 </widget>
956- <packing>
957- <property name="position">2</property>
958- </packing>
959 </child>
960 <child>
961- <widget class="kiwi+ui+widgets+label+Label" id="total_label">
962- <property name="data_type">unicode</property>
963- <property name="label" context="yes" translatable="yes">$20.40</property>
964+ <widget class="ProxyLabel" id="difference_label">
965+ <property name="data_type">currency</property>
966+ <property name="label" context="yes" translatable="yes">$0.00</property>
967+ <property name="model_attribute">difference_label</property>
968 <property name="visible">True</property>
969+ <property name="xalign">1.0</property>
970 </widget>
971 <packing>
972 <property name="expand">False</property>
973- <property name="position">3</property>
974+ <property name="position">1</property>
975 </packing>
976 </child>
977 </widget>
978 <packing>
979 <property name="expand">False</property>
980+ <property name="fill">False</property>
981 <property name="position">1</property>
982 </packing>
983 </child>
984+ <child>
985+ <widget class="GtkHBox" id="hbox2">
986+ <property name="border_width">5</property>
987+ <property name="spacing">5</property>
988+ <property name="visible">True</property>
989+ <child>
990+ <widget class="ProxyLabel" id="total_status_label">
991+ <property name="label" context="yes" translatable="yes">Total:</property>
992+ <property name="model_attribute">total_status_label</property>
993+ <property name="visible">True</property>
994+ <property name="xalign">1.0</property>
995+ </widget>
996+ </child>
997+ <child>
998+ <widget class="ProxyLabel" id="total_label">
999+ <property name="data_type">currency</property>
1000+ <property name="label" context="yes" translatable="yes">$0.00</property>
1001+ <property name="model_attribute">total_label</property>
1002+ <property name="visible">True</property>
1003+ <property name="xalign">1.0</property>
1004+ </widget>
1005+ <packing>
1006+ <property name="expand">False</property>
1007+ <property name="position">1</property>
1008+ </packing>
1009+ </child>
1010+ </widget>
1011+ <packing>
1012+ <property name="expand">False</property>
1013+ <property name="fill">False</property>
1014+ <property name="position">2</property>
1015+ </packing>
1016+ </child>
1017 </widget>
1018 </child>
1019 </widget>
1020
1021=== modified file 'data/glade/PurchasePaymentStep.glade'
1022--- data/glade/PurchasePaymentStep.glade 2008-10-22 19:59:27 +0000
1023+++ data/glade/PurchasePaymentStep.glade 2011-02-01 20:17:04 +0000
1024@@ -5,6 +5,7 @@
1025 <property name="border_width">5</property>
1026 <property name="default_height">250</property>
1027 <property name="default_width">440</property>
1028+ <property name="mnemonics_visible">False</property>
1029 <child>
1030 <widget class="GtkVBox" id="main_box">
1031 <property name="border_width">5</property>
1032@@ -61,72 +62,6 @@
1033 <property name="position">1</property>
1034 </packing>
1035 </child>
1036- <child>
1037- <widget class="GtkHBox" id="hbox1">
1038- <property name="border_width">5</property>
1039- <property name="spacing">5</property>
1040- <property name="visible">True</property>
1041- <child>
1042- <widget class="ProxyLabel" id="kiwilabel3">
1043- <property name="data_type">unicode</property>
1044- <property name="label" context="yes" translatable="yes">Subtotal:</property>
1045- <property name="model_attribute">kiwilabel3</property>
1046- <property name="visible">True</property>
1047- <property name="xalign">1.0</property>
1048- </widget>
1049- </child>
1050- <child>
1051- <widget class="ProxyLabel" id="subtotal_lbl">
1052- <property name="data_type">currency</property>
1053- <property name="label" context="yes" translatable="yes">0.00</property>
1054- <property name="model_attribute">purchase_subtotal</property>
1055- <property name="visible">True</property>
1056- <property name="xalign">1.0</property>
1057- </widget>
1058- <packing>
1059- <property name="expand">False</property>
1060- <property name="position">1</property>
1061- </packing>
1062- </child>
1063- </widget>
1064- <packing>
1065- <property name="expand">False</property>
1066- <property name="position">2</property>
1067- </packing>
1068- </child>
1069- <child>
1070- <widget class="GtkHBox" id="hbox7">
1071- <property name="border_width">5</property>
1072- <property name="spacing">5</property>
1073- <property name="visible">True</property>
1074- <child>
1075- <widget class="ProxyLabel" id="kiwilabel5">
1076- <property name="data_type">unicode</property>
1077- <property name="label" context="yes" translatable="yes">Total:</property>
1078- <property name="model_attribute">kiwilabel5</property>
1079- <property name="visible">True</property>
1080- <property name="xalign">1.0</property>
1081- </widget>
1082- </child>
1083- <child>
1084- <widget class="ProxyLabel" id="total_lbl">
1085- <property name="data_type">currency</property>
1086- <property name="label" context="yes" translatable="yes">0.00</property>
1087- <property name="model_attribute">purchase_total</property>
1088- <property name="visible">True</property>
1089- <property name="xalign">1.0</property>
1090- </widget>
1091- <packing>
1092- <property name="expand">False</property>
1093- <property name="position">1</property>
1094- </packing>
1095- </child>
1096- </widget>
1097- <packing>
1098- <property name="expand">False</property>
1099- <property name="position">3</property>
1100- </packing>
1101- </child>
1102 </widget>
1103 </child>
1104 </widget>
1105
1106=== modified file 'stoqlib/database/admin.py'
1107--- stoqlib/database/admin.py 2008-12-16 19:04:09 +0000
1108+++ stoqlib/database/admin.py 2011-02-01 20:17:04 +0000
1109@@ -59,7 +59,7 @@
1110 from stoqlib.importers.invoiceimporter import InvoiceImporter
1111 from stoqlib.lib.interfaces import IPaymentOperationManager
1112 from stoqlib.lib.message import error
1113-from stoqlib.lib.paymentoperation import PaymentOperationManager
1114+from stoqlib.lib.payment import PaymentOperationManager
1115 from stoqlib.lib.parameters import sysparam, ensure_system_parameters
1116 from stoqlib.lib.translation import stoqlib_gettext
1117
1118
1119=== modified file 'stoqlib/database/testsuite.py'
1120--- stoqlib/database/testsuite.py 2009-09-25 20:13:37 +0000
1121+++ stoqlib/database/testsuite.py 2011-02-01 20:17:04 +0000
1122@@ -45,7 +45,7 @@
1123 ISystemNotifier)
1124 from stoqlib.lib.message import DefaultSystemNotifier
1125 from stoqlib.lib.parameters import sysparam
1126-from stoqlib.lib.paymentoperation import PaymentOperationManager
1127+from stoqlib.lib.payment import PaymentOperationManager
1128
1129 log = Logger('stoqlib.database.testsuite')
1130
1131
1132=== modified file 'stoqlib/domain/payment/method.py'
1133--- stoqlib/domain/payment/method.py 2010-11-22 14:16:09 +0000
1134+++ stoqlib/domain/payment/method.py 2011-02-01 20:17:04 +0000
1135@@ -219,9 +219,10 @@
1136 # They should either go into the group or to a separate payment
1137 # factory singleton.
1138 @argcheck(object, PaymentGroup, Decimal, datetime.datetime,
1139- basestring, basestring, Till)
1140+ basestring, basestring, Till, basestring)
1141 def create_payment(self, iface, payment_group, value, due_date=None,
1142- description=None, base_value=None, till=None):
1143+ description=None, base_value=None, till=None,
1144+ payment_number=None):
1145 """Creates a new payment according to a payment method interface
1146 @param iface: a payment method interface eg L{IOutPayment} or
1147 L{IInPayment}
1148@@ -232,6 +233,7 @@
1149 @param description: optional, description of the payment
1150 @param base_value: optional
1151 @param till: optional
1152+ @param payment_number: optional
1153 @returns: a L{PaymentAdaptToOutPayment} or L{PaymentAdaptToInPayment}
1154 """
1155 conn = self.get_connection()
1156@@ -277,7 +279,8 @@
1157 method=self,
1158 category=None,
1159 till=till,
1160- description=description)
1161+ description=description,
1162+ payment_number=payment_number)
1163 facet = payment.addFacet(iface, connection=conn)
1164 self.operation.payment_create(payment)
1165 return facet
1166
1167=== modified file 'stoqlib/domain/payment/operation.py'
1168--- stoqlib/domain/payment/operation.py 2009-02-11 13:56:34 +0000
1169+++ stoqlib/domain/payment/operation.py 2011-02-01 20:17:04 +0000
1170@@ -233,7 +233,7 @@
1171 def register_payment_operations():
1172 pmm = get_utility(IPaymentOperationManager, None)
1173 if pmm is None:
1174- from stoqlib.lib.paymentoperation import PaymentOperationManager
1175+ from stoqlib.lib.payment import PaymentOperationManager
1176 pmm = PaymentOperationManager()
1177 provide_utility(IPaymentOperationManager, pmm)
1178 pmm.register('money', MoneyPaymentOperation())
1179
1180=== modified file 'stoqlib/domain/payment/payment.py'
1181--- stoqlib/domain/payment/payment.py 2010-12-16 13:54:46 +0000
1182+++ stoqlib/domain/payment/payment.py 2011-02-01 20:17:04 +0000
1183@@ -34,7 +34,8 @@
1184
1185 from stoqlib.database.orm import (IntCol, DateTimeCol, UnicodeCol, ForeignKey,
1186 PriceCol, DecimalCol)
1187-from stoqlib.database.orm import const, DESC, AND, OR, MultipleJoin
1188+from stoqlib.database.orm import (const, DESC, AND, OR, MultipleJoin,
1189+ SingleJoin)
1190 from stoqlib.domain.base import Domain, ModelAdapter
1191 from stoqlib.domain.interfaces import IInPayment, IOutPayment
1192 from stoqlib.exceptions import DatabaseInconsistency, StoqlibError
1193@@ -101,6 +102,7 @@
1194 category = ForeignKey('PaymentCategory')
1195
1196 comments = MultipleJoin('PaymentComment')
1197+ check_data = SingleJoin('CheckData', joinColumn='payment_id')
1198
1199 def _check_status(self, status, operation_name):
1200 assert self.status == status, ('Invalid status for %s '
1201
1202=== modified file 'stoqlib/gui/slaves/paymentslave.py'
1203--- stoqlib/gui/slaves/paymentslave.py 2011-01-26 19:43:02 +0000
1204+++ stoqlib/gui/slaves/paymentslave.py 2011-02-01 20:17:04 +0000
1205@@ -22,22 +22,26 @@
1206 ## Author(s): Evandro Vale Miquelito <evandro@async.com.br>
1207 ## Johan Dahlin <jdahlin@async.com.br>
1208 ## George Kussumoto <george@async.com.br>
1209+## Thiago Bellini <hackedbellini@async.com.br>
1210 ##
1211 ##
1212 """ Slaves for payment management """
1213
1214 import gtk
1215
1216-from decimal import Decimal
1217+
1218+from copy import deepcopy
1219 import datetime
1220 from dateutil.relativedelta import relativedelta
1221+from decimal import Decimal
1222
1223 from kiwi import ValueUnset
1224+from kiwi.component import get_utility
1225 from kiwi.datatypes import format_price, currency, ValidationError
1226-from kiwi.component import get_utility
1227 from kiwi.python import Settable
1228 from kiwi.utils import gsignal
1229-from kiwi.ui.objectlist import Column
1230+from kiwi.ui.delegates import GladeDelegate, GladeSlaveDelegate
1231+from kiwi.ui.objectlist import ObjectList, Column, SequentialColumn
1232 from kiwi.ui.views import SlaveView
1233
1234 from stoqlib.database.runtime import get_connection
1235@@ -58,394 +62,403 @@
1236 from stoqlib.gui.editors.baseeditor import BaseEditorSlave, BaseEditor
1237 from stoqlib.gui.interfaces import IDomainSlaveMapper
1238 from stoqlib.lib.defaults import (interval_types, INTERVALTYPE_MONTH,
1239- DECIMAL_PRECISION, calculate_interval)
1240+ DECIMAL_PRECISION)
1241 from stoqlib.lib.message import info, warning
1242+from stoqlib.lib.payment import (generate_payments_values,
1243+ generate_payments_due_dates)
1244 from stoqlib.lib.translation import stoqlib_gettext
1245
1246 _ = stoqlib_gettext
1247
1248-
1249-class _TemporaryBillData(object):
1250- def __init__(self, group=None, first_duedate=None):
1251- self.group = group
1252- self.first_duedate = first_duedate
1253- self.installments_number = 1
1254- self.intervals = 1
1255- self.interval_type = 0
1256-
1257-
1258-class _TemporaryMoneyData(object):
1259- def __init__(self):
1260- self.first_duedate = datetime.datetime.today()
1261- self.installments_number = 1
1262- self.intervals = 1
1263-
1264-
1265-class _TemporaryStoreCreditData(object):
1266- def __init__(self):
1267- self.first_duedate = datetime.datetime.today()
1268- self.installments_number = 1
1269- self.intervals = 1
1270-
1271-
1272-class _TemporaryCreditProviderGroupData(object):
1273- def __init__(self, group, provider):
1274- self.installments_number = 1
1275+#
1276+# Temporary Objects
1277+#
1278+
1279+DEFAULT_INSTALLMENTS_NUMBER = 1
1280+DEFAULT_INTERVALS = 1
1281+DEFAULT_INTERVAL_TYPE = INTERVALTYPE_MONTH
1282+
1283+class _BaseTemporaryMethodData(object):
1284+ def __init__(self):
1285+ self.first_duedate = datetime.date.today()
1286+ self.installments_number = DEFAULT_INSTALLMENTS_NUMBER
1287+ self.intervals = DEFAULT_INTERVALS
1288+ self.interval_type = DEFAULT_INTERVAL_TYPE
1289+
1290+
1291+class _TemporaryCreditProviderGroupData(_BaseTemporaryMethodData):
1292+ def __init__(self, provider=None):
1293 self.provider = provider
1294- self.group = group
1295-
1296-
1297-class PaymentListSlave(BaseEditorSlave):
1298- """A basic payment list slave. Each element of this list is a payment
1299- method slave which hold informations about payments. Available slaves
1300- are: BillDataSlave and CheckDataSlave
1301-
1302- Notes:
1303- - get_payment_slave: is a hook method which must be defined in
1304- parents. The result of this function must
1305- be a BaseEditorSlave instance.
1306- """
1307-
1308- gladefile = 'PaymentListSlave'
1309- model_type = PaymentMethod
1310-
1311- gsignal('remove-slave')
1312- gsignal('add-slave')
1313- gsignal('remove-item', SlaveView)
1314-
1315- def __init__(self, parent, conn, payment_method, total_amount):
1316- self.parent = parent
1317- self.total_amount = total_amount
1318- self.max_installments = None
1319- # This dict stores a reference of each toplevel widget with its own
1320- # kiwi object, the slave.
1321- self.payment_slaves = {}
1322- BaseEditorSlave.__init__(self, conn, payment_method)
1323- self.update_view()
1324-
1325- #
1326- # Private
1327- #
1328-
1329- def _remove_payment_slave(self, widget):
1330- slave = self.payment_slaves[widget]
1331- del self.payment_slaves[widget]
1332- self.list_vbox.remove(widget)
1333- self.update_view()
1334- self.emit("remove-item", slave)
1335-
1336- def _remove_last_payment_slave(self):
1337- vbox_children = self.list_vbox.get_children()
1338- if not vbox_children:
1339- return
1340- widget = vbox_children[-1]
1341- self._remove_payment_slave(widget)
1342-
1343- #
1344- # Public API
1345- #
1346-
1347- def get_total_difference(self):
1348- """Get the difference for the total of check payments invoiced. If
1349- the difference is zero the entire sale total value is invoiced.
1350- If the difference is greater than zero, there is an outstanding
1351- amount to invoice. If the value is negative, there is a overpaid
1352- value.
1353- """
1354- slaves = self.payment_slaves.values()
1355- values = [s.get_payment_value() for s in slaves
1356- if s.get_payment_value() is not None]
1357- total = sum(values, currency(0))
1358- slaves_total = Decimal(str(total))
1359- slaves_total -= self.parent.get_interest_total()
1360- if slaves_total == self.total_amount:
1361- return currency(0)
1362- return currency(self.total_amount - slaves_total)
1363-
1364- def update_view(self):
1365- children_number = self.get_children_number()
1366- can_remove = children_number > 1
1367- max = self.max_installments or 0
1368- can_add = children_number < max
1369- self.remove_button.set_sensitive(can_remove)
1370- self.add_button.set_sensitive(can_add)
1371- self.update_total_label()
1372-
1373- def update_total_label(self):
1374- difference = self.get_total_difference()
1375- if not round(difference, DECIMAL_PRECISION):
1376- label_name = difference = ''
1377- elif difference < 0:
1378- difference *= -1
1379- label_name = _('Overpaid:')
1380- else:
1381- label_name = _('Outstanding:')
1382- if difference:
1383- difference = format_price(difference)
1384- self.total_label.set_text(difference)
1385- self.status_label.set_text(label_name)
1386-
1387- def get_children_number(self):
1388- vbox_children = self.list_vbox.get_children()
1389- return len(vbox_children)
1390-
1391- def register_max_installments(self, inst_number):
1392- self.max_installments = inst_number
1393-
1394- def clear_list(self):
1395- for widget in self.list_vbox.get_children()[:]:
1396- self._remove_payment_slave(widget)
1397-
1398- def update_payment_list(self, installments_number):
1399- installments_number = installments_number or 0
1400- children_number = self.get_children_number()
1401- difference = installments_number - children_number
1402- if not difference:
1403- return
1404- if difference > 0:
1405- for unused in range(difference):
1406- self.add_slave()
1407- else:
1408- difference *= -1
1409- for unused in range(difference):
1410- self._remove_last_payment_slave()
1411-
1412- def add_slave(self, slave=None):
1413- if not self.max_installments:
1414- raise ValueError('You call register_max_installments '
1415- 'before start adding slaves')
1416- if self.get_children_number() > self.max_installments:
1417- return
1418- slave = slave or self.parent.get_payment_slave()
1419- widget = slave.get_toplevel()
1420- self.payment_slaves[widget] = slave
1421- children_number = self.get_children_number() + 1
1422- slave.set_frame_label('# %d' % children_number)
1423- self.list_vbox.pack_start(widget, False)
1424- # Scroll to the bottom of the scrolled window
1425- vadj = self.scrolled_window.get_vadjustment()
1426- vadj.set_value(vadj.upper)
1427- widget.show()
1428- self.update_view()
1429-
1430- def is_all_due_dates_valid(self):
1431- today = datetime.date.today()
1432- for slave in self.payment_slaves.values():
1433- if slave.due_date.read() < today:
1434- return False
1435- return True
1436-
1437- #
1438- # Kiwi callbacks
1439- #
1440-
1441- def on_add_button__clicked(self, *args):
1442- self.add_slave()
1443- self.emit('add-slave')
1444-
1445- def on_remove_button__clicked(self, *args):
1446- self._remove_last_payment_slave()
1447- self.emit('remove-slave')
1448-
1449-
1450-class BankDataSlave(BaseEditorSlave):
1451- """ A simple slave that contains only a hbox with fields to bank name and
1452- its branch. This slave is used by payment method slaves that has reference
1453- to a BankAccount object.
1454- """
1455- gladefile = 'BankDataSlave'
1456- model_type = BankAccount
1457- proxy_widgets = ('bank', 'branch')
1458-
1459- #
1460- # BaseEditorSlave hooks
1461- #
1462-
1463- def setup_proxies(self):
1464- self.add_proxy(self.model, BankDataSlave.proxy_widgets)
1465-
1466-
1467-class BillDataSlave(BaseEditorSlave):
1468- """ A slave to set payment information of bill payment method.
1469- """
1470-
1471- gladefile = 'BillDataSlave'
1472- model_type = Payment
1473+ _BaseTemporaryMethodData.__init__(self)
1474+
1475+
1476+class _TemporaryPaymentData(object):
1477+ def __init__(self, description, value, due_date,
1478+ payment_number=None, bank_data=None):
1479+ self.description = description
1480+ self.value = value
1481+ self.due_date = due_date
1482+ self.payment_number = payment_number
1483+ self.bank_data = bank_data
1484+
1485+
1486+class _TemporaryBankData(object):
1487+ def __init__(self, bank_id=None, bank_branch=None, bank_account=None):
1488+ self.bank_id = bank_id
1489+ self.bank_branch = bank_branch
1490+ self.bank_account = bank_account
1491+
1492+
1493+#
1494+# Editors
1495+#
1496+
1497+class BasePaymentDataEditor(BaseEditor):
1498+ """A base editor to set payment information.
1499+ """
1500+
1501+ gladefile = 'BasePaymentDataEditor'
1502+ model_type = _TemporaryPaymentData
1503 payment_widgets = ('due_date', 'value', 'payment_number')
1504- gsignal('paymentvalue-changed')
1505- gsignal('duedate-validate')
1506-
1507- def __init__(self, conn, payment_group, due_date, value,
1508- method_iface, model=None):
1509- self._payment_group = payment_group
1510- self._due_date = due_date
1511- self._value = value
1512- self._method_iface = method_iface
1513- BaseEditorSlave.__init__(self, conn, model)
1514-
1515- def _setup_widgets(self):
1516- self.payment_number_label.set_bold(True)
1517- self.payment_number_label.set_size('small')
1518-
1519- def set_frame_label(self, label_name):
1520- self.payment_number_label.set_text(label_name)
1521-
1522- def get_payment_value(self):
1523- return self.model.value
1524+ slave_holder = 'bank_data_slave'
1525+
1526+ def __init__(self, model):
1527+ BaseEditor.__init__(self, None, model)
1528+
1529+ def get_title(self, model):
1530+ return _(u"Edit '%s'" % model.description)
1531
1532 #
1533 # BaseEditorSlave hooks
1534 #
1535
1536- def create_model(self, conn):
1537- bill_method = PaymentMethod.get_by_name(conn, 'bill')
1538- apayment = bill_method.create_payment(self._method_iface,
1539- self._payment_group,
1540- self._value,
1541- self._due_date)
1542- return apayment.get_adapted()
1543-
1544 def setup_proxies(self):
1545- self._setup_widgets()
1546- self.add_proxy(self.model, BillDataSlave.payment_widgets)
1547+ self.add_proxy(self.model, self.payment_widgets)
1548
1549 #
1550 # Kiwi callbacks
1551 #
1552
1553- def after_value__changed(self, *args):
1554- self.emit('paymentvalue-changed')
1555-
1556 def on_due_date__validate(self, widget, value):
1557- self.emit('duedate-validate')
1558 if value < datetime.date.today():
1559 return ValidationError(_(u"Expected installment due date "
1560 "must be set to a future date"))
1561
1562-
1563-class CheckDataSlave(BillDataSlave):
1564- """A slave to set payment information of check payment method."""
1565- slave_holder = 'bank_data_slave'
1566- model_type = CheckData
1567-
1568- def __init__(self, conn, payment_group, due_date, value,
1569- is_sale_payment, model=None, default_bank=None):
1570+ def on_value__validate(self, widget, value):
1571+ if value < currency(0):
1572+ return ValidationError(_(u"The value must be "
1573+ "a positive number"))
1574+
1575+
1576+class CheckDataEditor(BasePaymentDataEditor):
1577+ """An editor to set payment information of check payment method.
1578+ """
1579+
1580+ def __init__(self, model, default_bank=None):
1581 self._default_bank = default_bank
1582- BillDataSlave.__init__(self, conn, payment_group, due_date,
1583- value, is_sale_payment, model)
1584+ BasePaymentDataEditor.__init__(self, model)
1585
1586 #
1587 # BaseEditorSlave hooks
1588 #
1589
1590- def get_payment_value(self):
1591- return self.model.payment.value
1592-
1593- def create_model(self, conn):
1594- check_method = PaymentMethod.get_by_name(conn, 'check')
1595- payment = check_method.create_payment(self._method_iface,
1596- self._payment_group,
1597- self._value, self._due_date)
1598- return check_method.operation.get_check_data_by_payment(
1599- payment.get_adapted())
1600-
1601 def setup_slaves(self):
1602 if self._default_bank and not self.model.bank_data.bank_id:
1603 self.model.bank_data.bank_id = self._default_bank
1604- bank_data_slave = BankDataSlave(self.conn, self.model.bank_data)
1605+ bank_data_slave = BankDataSlave(self.model.bank_data)
1606+
1607 if self.get_slave(self.slave_holder):
1608 self.detach_slave(self.slave_holder)
1609 self.attach_slave(self.slave_holder, bank_data_slave)
1610
1611+
1612+class BankDataSlave(BaseEditorSlave):
1613+ """A simple slave that contains only a hbox with fields to bank name and
1614+ its branch. This slave is used by payment method slaves that has reference
1615+ to a BankAccount object.
1616+ """
1617+
1618+ gladefile = 'BankDataSlave'
1619+ model_type = _TemporaryBankData
1620+ proxy_widgets = ('bank_id', 'bank_branch', 'bank_account')
1621+
1622+ def __init__(self, model):
1623+ self.model = model
1624+ BaseEditorSlave.__init__(self, None, self.model)
1625+
1626+ #
1627+ # BaseEditorSlave hooks
1628+ #
1629+
1630 def setup_proxies(self):
1631+ self.add_proxy(self.model, self.proxy_widgets)
1632+
1633+
1634+class PaymentListSlave(GladeSlaveDelegate):
1635+ """A slave to manage payments with one/multiple installment(s)"""
1636+
1637+ gladefile = 'PaymentListSlave'
1638+ gsignal('payment-edited')
1639+
1640+ def __init__(self, iface, group, method, total_value, editor_class, parent):
1641+ self.parent = parent
1642+ self.iface = iface
1643+ self.group = group
1644+ self.total_value = total_value
1645+ self.editor_class = editor_class
1646+ self.method = method
1647+
1648+ GladeSlaveDelegate.__init__(self, gladefile=self.gladefile)
1649 self._setup_widgets()
1650- self.add_proxy(self.model.payment, BillDataSlave.payment_widgets)
1651-
1652+
1653+ #
1654+ # Private Methods
1655+ #
1656+
1657+ def _can_edit_payments(self):
1658+ return self.method.method_name != 'money'
1659+
1660+ def _get_columns(self):
1661+ columns = [Column('description', title=_('Description'),
1662+ expand=True, data_type=str)]
1663+ # Add columns to show bank data when the method needs it (checks for
1664+ # example.
1665+ if self.method.method_name == 'check':
1666+ columns.extend([Column('bank_data.bank_id',
1667+ title=_('Bank ID'),
1668+ data_type=int, justify=gtk.JUSTIFY_RIGHT),
1669+ Column('bank_data.bank_branch',
1670+ title=_('Bank Branch'),
1671+ data_type=str, justify=gtk.JUSTIFY_RIGHT),
1672+ Column('bank_data.bank_account',
1673+ title=_('Bank Account'),
1674+ data_type=str, justify=gtk.JUSTIFY_RIGHT)])
1675+ # Money methods doesn't have a payment_number related with it.
1676+ if self.method.method_name != 'money':
1677+ columns.append(Column('payment_number', title=_('Number'),
1678+ data_type=str, justify=gtk.JUSTIFY_RIGHT))
1679+
1680+ columns.extend([Column('due_date', title=_('Due Date'),
1681+ data_type=datetime.date),
1682+ Column('value', title=_('Value'), data_type=currency,
1683+ justify=gtk.JUSTIFY_RIGHT)])
1684+
1685+ return columns
1686+
1687+
1688+ def _setup_widgets(self):
1689+ self.payment_list.set_columns(self._get_columns())
1690+ self.total_label.set_text(format_price(self.total_value, True,
1691+ DECIMAL_PRECISION))
1692+
1693+ def _update_difference_label(self):
1694+ difference = self.get_total_difference()
1695+ if not difference:
1696+ label_name = _('Difference')
1697+ elif difference > 0:
1698+ label_name = _('Overpaid:')
1699+ elif difference < 0:
1700+ label_name = _('Outstanding:')
1701+ difference *= -1
1702+ difference = format_price(difference, True, DECIMAL_PRECISION)
1703+ self.difference_label.set_text(difference)
1704+ self.difference_status_label.set_text(label_name)
1705+
1706+ def _run_edit_payment_dialog(self):
1707+ if not self._can_edit_payments():
1708+ return
1709+
1710+ payment = self.payment_list.get_selected()
1711+ old = deepcopy(payment)
1712+ retval = run_dialog(self.editor_class, self.parent, payment)
1713+ if not retval:
1714+ # Remove the changes if dialog was canceled.
1715+ pos = self.payment_list.get_selected_row_number()
1716+ self.payment_list.remove(payment)
1717+ self.payment_list.insert(pos, old)
1718+ self.emit('payment-edited')
1719+
1720+ #
1721+ # Public API
1722+ #
1723+
1724+ def update_view(self):
1725+ self.payment_list.refresh()
1726+ self._update_difference_label()
1727+
1728+ def add_payments(self, installments_number, first_due_date,
1729+ interval, interval_type):
1730+ values = generate_payments_values(self.total_value,
1731+ installments_number)
1732+ due_dates = generate_payments_due_dates(installments_number,
1733+ first_due_date, interval,
1734+ interval_type)
1735+ bank_data = None
1736+
1737+ self.clear_payments()
1738+ for i in range(installments_number):
1739+ description = self.method.describe_payment(self.group, i + 1,
1740+ installments_number)
1741+ if self.method.method_name == 'check':
1742+ bank_data = _TemporaryBankData()
1743+ payment = _TemporaryPaymentData(description,
1744+ currency(values[i]),
1745+ due_dates[i],
1746+ None,
1747+ bank_data)
1748+ self.payment_list.append(payment)
1749+
1750+ self.update_view()
1751+
1752+ def create_payments(self):
1753+ if not self.is_payment_list_valid():
1754+ return []
1755+
1756+ payments = []
1757+ for p in self.payment_list:
1758+ # FIXME: The create_payment API has an argcheck on due_date
1759+ # forcing it to be datetime.datetime, so we need to
1760+ # make this conversion.
1761+ due_date = datetime.datetime(p.due_date.year,
1762+ p.due_date.month,
1763+ p.due_date.day)
1764+ payment = self.method.create_payment(iface=self.iface,
1765+ payment_group=self.group,
1766+ value=p.value,
1767+ due_date=due_date,
1768+ description=p.description,
1769+ payment_number=p.payment_number)
1770+ if p.bank_data:
1771+ # Add the bank_data into the payment, if any.
1772+ adapted = payment.get_adapted()
1773+ bank_data = adapted.check_data.bank_data
1774+ bank_data.bank_id = p.bank_data.bank_id
1775+ bank_data.branch = p.bank_data.bank_branch
1776+ bank_data.account = p.bank_data.bank_account
1777+ payments.append(payment)
1778+
1779+ return payments
1780+
1781+ def clear_payments(self):
1782+ self.payment_list.clear();
1783+ self.update_view()
1784+
1785+ def get_total_difference(self):
1786+ total_payments = Decimal(0)
1787+ for payment in self.payment_list:
1788+ total_payments += payment.value
1789+ return (total_payments - self.total_value)
1790+
1791+ def are_due_dates_valid(self):
1792+ previous_date = datetime.date.today() + datetime.timedelta(days=-1)
1793+ for payment in self.payment_list:
1794+ if payment.due_date <= previous_date:
1795+ warning(_(u"Payment dates can't repeat or be lower than "
1796+ "previous dates."))
1797+ return False
1798+ previous_date = payment.due_date
1799+ return True
1800+
1801+ def are_payment_values_valid(self):
1802+ return not self.get_total_difference()
1803+
1804+ def is_payment_list_valid(self):
1805+ if not self.are_due_dates_valid():
1806+ return False
1807+ if not self.are_payment_values_valid():
1808+ return False
1809+ return True
1810+
1811+ #
1812+ # Kiwi Callbacks
1813+ #
1814+
1815+ def on_payment_list__row_activated(self, *args):
1816+ self._run_edit_payment_dialog()
1817+ self.update_view()
1818+
1819+
1820+#
1821+# Payment Method Slaves
1822+#
1823
1824 class BasePaymentMethodSlave(BaseEditorSlave):
1825 """A base payment method slave for Bill and Check methods."""
1826
1827- gladefile = 'BillCheckMethodSlave'
1828- model_type = _TemporaryBillData
1829- slave_holder = 'bill_check_data_list'
1830+ gladefile = 'BasePaymentMethodSlave'
1831+ model_type = _BaseTemporaryMethodData
1832+ slave_holder = 'slave_holder'
1833 proxy_widgets = ('interval_type_combo',
1834 'intervals',
1835 'first_duedate',
1836 'installments_number')
1837- # This attribute must be defined in child. It can assume two
1838- # value: CheckDataSlave, BillDataSlave
1839- _data_slave_class = None
1840+
1841+ # This attribute must be defined in child.
1842+ # Use BasePaymentDataEditor or one of it's children.
1843+ _data_editor_class = None
1844
1845 def __init__(self, wizard, parent, conn, order_obj, payment_method,
1846 outstanding_value=currency(0)):
1847+ self.wizard = wizard
1848+ self.parent = parent
1849 # Note that 'order' may be a Sale or a PurchaseOrder object
1850 self.order = order_obj
1851- self.wizard = wizard
1852 self.method = payment_method
1853 self.method_iface = self._get_payment_method_iface()
1854- # This is very useful when calculating the total amount outstanding
1855- # or overpaid of the payments
1856- self.interest_total = currency(0)
1857+ self.total_value = outstanding_value or self._get_total_amount()
1858 self.payment_group = self.order.group
1859 self.payment_list = None
1860- self._reset_btn_validation_ok = True
1861- self.total_value = outstanding_value or self._get_total_amount()
1862+ # This is very useful when calculating the total amount outstanding
1863+ # or overpaid of the payments
1864+ self.interest_total = currency(0)
1865+
1866 BaseEditorSlave.__init__(self, conn)
1867 self.register_validate_function(self._refresh_next)
1868- self.parent = parent
1869- self.interval_type_combo.set_sensitive(False)
1870- self.intervals.set_sensitive(False)
1871- self.update_view()
1872+
1873+ #
1874+ # Private Methods
1875+ #
1876
1877 def _refresh_next(self, validation_ok=True):
1878- if validation_ok and self.payment_list:
1879- total_difference = self.payment_list.get_total_difference()
1880- validation_ok = (total_difference == currency(0) and
1881- self.payment_list.is_all_due_dates_valid())
1882+ if not self.payment_list:
1883+ validation_ok = False
1884+ if validation_ok:
1885+ validation_ok = self.payment_list.is_payment_list_valid()
1886+
1887 self.wizard.refresh_next(validation_ok)
1888
1889- def update_view(self):
1890- attrs = [self.model.installments_number, self.model.first_duedate,
1891- self.model.intervals]
1892- self.reset_button.set_sensitive((None not in attrs) and
1893- self._reset_btn_validation_ok)
1894- self._refresh_next()
1895-
1896- def _setup_widgets(self):
1897- max = self.method.max_installments
1898- self.installments_number.set_range(1, max)
1899- self.installments_number.set_value(1)
1900-
1901- items = [(label, constant) for constant, label
1902- in interval_types.items()]
1903- self.interval_type_combo.prefill(items)
1904- self.payment_list = PaymentListSlave(self, self.conn,
1905- self.method, self.total_value)
1906- self.payment_list.connect('add-slave',
1907- self.update_installments_number)
1908- self.payment_list.connect('remove-slave',
1909- self.update_installments_number)
1910- self.payment_list.connect("remove-item",
1911- self._on_payment_list__remove_item)
1912- self.payment_list.register_max_installments(max)
1913+ def _setup_payment_list(self):
1914+ self.payment_list = PaymentListSlave(self.method_iface,
1915+ self.payment_group,
1916+ self.method,
1917+ self.total_value,
1918+ self._data_editor_class,
1919+ self.wizard)
1920 if self.get_slave(BasePaymentMethodSlave.slave_holder):
1921 self.detach_slave(BasePaymentMethodSlave.slave_holder)
1922 self.attach_slave(BasePaymentMethodSlave.slave_holder,
1923 self.payment_list)
1924- created_adapted_payments = self.get_created_adapted_payments()
1925- if created_adapted_payments:
1926- self.fill_slave_list(created_adapted_payments)
1927- else:
1928- # Adding the first payment
1929- slave = self.get_payment_slave()
1930- self.payment_list.add_slave(slave)
1931-
1932- def get_created_adapted_payments(self):
1933- for payment in Payment.selectBy(group=self.payment_group,
1934- method=self.method,
1935- status=Payment.STATUS_PREVIEW,
1936- connection=self.conn):
1937- yield self.method_iface(payment, None)
1938+ self.setup_payments()
1939+ self.payment_list.connect('payment-edited',
1940+ self._on_payment_list__edit_payment)
1941+
1942+
1943+ def _setup_widgets(self):
1944+ max_installments = self.method.max_installments
1945+ self.installments_number.set_range(1, max_installments)
1946+ self.installments_number.set_value(1)
1947+ # FIXME: Workarround to make intervals never go to 0
1948+ self.intervals.set_range(1, 99)
1949+
1950+ self.intervals.set_sensitive(False)
1951+ items = [(label, constant)
1952+ for constant, label in interval_types.items()]
1953+ self.interval_type_combo.prefill(items)
1954+ self.interval_type_combo.select_item_by_data(INTERVALTYPE_MONTH)
1955+ self.interval_type_combo.set_sensitive(False)
1956+
1957+ # PaymentListSlave setup
1958+ self._setup_payment_list()
1959
1960 def _get_total_amount(self):
1961 """Returns the order total amount """
1962@@ -468,98 +481,45 @@
1963 else:
1964 raise TypeError
1965
1966- #
1967- # General methods
1968- #
1969-
1970- def _setup_payments(self):
1971- self.payment_list.clear_list()
1972- due_dates = []
1973- interval = calculate_interval(self.model.interval_type,
1974- self.model.intervals)
1975- installments_number = self.model.installments_number
1976- self.payment_group.installments_number = installments_number
1977- due_date = self.model.first_duedate
1978- for i in range(installments_number):
1979- #XXX: convert to datetime.datetime
1980- d = datetime.datetime(due_date.year, due_date.month, due_date.day)
1981- due_dates.append(d + datetime.timedelta(i * interval))
1982-
1983- payments = self.method.create_payments(self.method_iface,
1984- self.payment_group,
1985- self.total_value,
1986- due_dates)
1987- interest = Decimal(0)
1988-
1989- # This is very useful when calculating the total amount outstanding
1990- # or overpaid of the payments
1991- self.interest_total = interest
1992- self.fill_slave_list(payments)
1993-
1994- def fill_slave_list(self, adapted_payments):
1995- for adapted in adapted_payments:
1996- slave = self.get_slave_by_adapted_payment(adapted)
1997- self.payment_list.add_slave(slave)
1998-
1999- def get_slave_by_adapted_payment(self, adapted_payment):
2000- raise NotImplementedError
2001+ def _create_payments(self):
2002+ """Insert the payment_list's payments in the base."""
2003+ return self.payment_list.create_payments()
2004+
2005+ #
2006+ # Public API
2007+ #
2008+
2009+ def setup_payments(self):
2010+ """Setup the payments in PaymentList.
2011+
2012+ Note: The payments are not inserted into the db until self.finish()
2013+ is called. The wizard is responsable for that"""
2014+ if not self.model.first_duedate:
2015+ return
2016+ if self.payment_list:
2017+ self.payment_list.add_payments(self.model.installments_number,
2018+ self.model.first_duedate,
2019+ self.model.intervals,
2020+ self.model.interval_type)
2021+ self.update_view()
2022+
2023+ def update_view(self):
2024+ self._refresh_next()
2025
2026 def get_interest_total(self):
2027 return self.interest_total
2028
2029- def get_extra_slave_args(self):
2030- """ This method can be redefined in child when extra parameters needs
2031- to be passed to the slave class. This method must return always a list
2032- with the parameters.
2033- """
2034- return []
2035-
2036- #
2037- # PaymentListSlave
2038- #
2039-
2040- def get_payment_slave(self, model=None):
2041- if not self._data_slave_class:
2042- raise ValueError('Child classes must define a data_slave_class '
2043- 'attribute')
2044- due_date = datetime.datetime.today()
2045- if not self.payment_list.get_children_number():
2046- total = self.total_value
2047- else:
2048- total = currency(0)
2049- extra_params = self.get_extra_slave_args()
2050- slave = self._data_slave_class(self.conn, self.payment_group, due_date,
2051- total, self.method_iface, model,
2052- *extra_params)
2053- slave.connect('paymentvalue-changed',
2054- self._on_slave__paymentvalue_changed)
2055- slave.connect('duedate-validate',
2056- self._on_slave__duedate_validate)
2057- return slave
2058-
2059- def update_installments_number(self, *args):
2060- inst_number = self.payment_list.get_children_number()
2061- self.model.installments_number = inst_number
2062- self.proxy.update('installments_number')
2063-
2064 #
2065 # PaymentMethodStep hooks
2066 #
2067
2068 def finish(self):
2069- # Since payments are created during this step there is no need to
2070- # perform tasks here
2071-
2072- # Validate payment dates before moving on
2073- today = datetime.date.today()
2074- previous_date = today + datetime.timedelta(days=-1)
2075- for slave in self.payment_list.payment_slaves.values():
2076- if (slave.due_date.read() < today or
2077- slave.due_date.read() <= previous_date):
2078- warning(_(u"Payment dates can't repeat or be lower than "
2079- "previous dates."))
2080- return False
2081- previous_date = slave.due_date.read()
2082+ """This method is called by the wizard when going to a next step.
2083+ If it returns False, the wizard can't go on."""
2084+ if (not self.payment_list or
2085+ not self.payment_list.is_payment_list_valid()):
2086+ return False
2087+ self._create_payments()
2088
2089 return True
2090
2091@@ -571,102 +531,66 @@
2092 self._setup_widgets()
2093 self.proxy = self.add_proxy(self.model,
2094 BasePaymentMethodSlave.proxy_widgets)
2095- self.interval_type_combo.select_item_by_data(INTERVALTYPE_MONTH)
2096
2097 def create_model(self, conn):
2098- return _TemporaryBillData(group=self.payment_group,
2099- first_duedate=datetime.datetime.today())
2100+ return _BaseTemporaryMethodData()
2101
2102 #
2103 # Kiwi callbacks
2104 #
2105
2106- def on_installments_number__changed(self, proxyspinbutton):
2107- # Call this callback *on* the value changed because we need to
2108- # have the same value for the length of the payments list and
2109- # validate the installments_number
2110- inst_number = self.model.installments_number
2111- max = self.method.max_installments
2112- if inst_number > max:
2113- self.installments_number.set_invalid(_("The number of installments "
2114- "must be less then %d" % max))
2115- self._refresh_next(False)
2116- return
2117- if self.payment_list:
2118- self.payment_list.update_payment_list(inst_number)
2119- has_installments = inst_number > 1
2120+ def _on_payment_list__edit_payment(self, *args):
2121+ """Callback for the 'payment-edited' signal on PaymentListSlave"""
2122+ self.update_view()
2123+
2124+ def after_installments_number__changed(self, *args):
2125+ has_installments = self.model.installments_number > 1
2126+
2127 self.interval_type_combo.set_sensitive(has_installments)
2128 self.intervals.set_sensitive(has_installments)
2129- self._refresh_next(False)
2130+ self.setup_payments()
2131+
2132+ def after_intervals__changed(self, *args):
2133+ self.setup_payments()
2134+
2135+ def after_interval_type_combo__changed(self, *args):
2136+ self.setup_payments()
2137+
2138+ def after_first_duedate__changed(self, *args):
2139+ self.setup_payments()
2140+
2141+ def on_installments_number__validate(self, widget, value):
2142+ if not value:
2143+ return ValidationError(_("The number of installments "
2144+ "cannot be 0"))
2145+
2146+ max_installments = self.method.max_installments
2147+ if value > max_installments:
2148+ return ValidationError(_("The number of installments "
2149+ "must be less then %d" %
2150+ max_installments))
2151
2152 def on_first_duedate__validate(self, widget, value):
2153 if value < datetime.date.today():
2154- return ValidationError(_("Expected first installment date must be set "
2155- "to a future date"))
2156- self._refresh_next(False)
2157-
2158- def on_intervals__value_changed(self, *args):
2159- self.update_view()
2160- self._refresh_next(False)
2161-
2162- def on_interval_type_combo__changed(self, *args):
2163- self.update_view()
2164- self._refresh_next(False)
2165-
2166- def on_reset_button__clicked(self, *args):
2167- self._setup_payments()
2168- self.update_view()
2169-
2170- def on_intervals__validation_changed(self, widget, is_valid):
2171- self._reset_btn_validation_ok = is_valid
2172- self.update_view()
2173-
2174- def on_first_duedate__validation_changed(self, widget, is_valid):
2175- self._reset_btn_validation_ok = is_valid
2176- self.update_view()
2177-
2178- def on_installments_number__validation_changed(self, widget, is_valid):
2179- self._reset_btn_validation_ok = is_valid
2180- self.update_view()
2181-
2182- def _on_slave__paymentvalue_changed(self, slave):
2183- self.update_view()
2184- self.payment_list.update_total_label()
2185-
2186- def _on_slave__duedate_validate(self, slave):
2187- self.update_view()
2188-
2189- def _on_payment_list__remove_item(self, payment_list, slave):
2190- if not isinstance(slave.model, slave.model_type):
2191- raise TypeError('Slave model attribute should be of type '
2192- '%s, got %s' % (slave.model_type,
2193- type(slave.model)))
2194-
2195- if isinstance(slave.model, CheckData):
2196- payment = slave.model.payment
2197- else:
2198- payment = slave.model
2199-
2200- Payment.delete(payment.id, self.conn)
2201+ self.payment_list.clear_payments()
2202+ return ValidationError(_("Expected first installment date must be "
2203+ "set to a future date"))
2204+
2205+
2206+class BillMethodSlave(BasePaymentMethodSlave):
2207+ _data_editor_class = BasePaymentDataEditor
2208+
2209+ def __init__(self, wizard, parent, conn, sale, payment_method,
2210+ outstanding_value=currency(0)):
2211+ BasePaymentMethodSlave.__init__(self, wizard, parent, conn,
2212+ sale, payment_method,
2213+ outstanding_value=outstanding_value)
2214+ self.bank_label.hide()
2215+ self.bank_combo.hide()
2216
2217
2218 class CheckMethodSlave(BasePaymentMethodSlave):
2219- _data_slave_class = CheckDataSlave
2220-
2221- def get_slave_by_adapted_payment(self, adapted_payment):
2222- check_data = self.method.operation.get_check_data_by_payment(
2223- adapted_payment.get_adapted())
2224- return self.get_payment_slave(check_data)
2225-
2226- def get_extra_slave_args(self):
2227- """ If there is any selected item in the banks combo, return this
2228- as extra parameter to the slave (CheckDataSlave). """
2229- if (self.bank_combo.get_property("visible")
2230- and len(self.bank_combo.get_model())):
2231- bank_id = self.bank_combo.get_selected()
2232- if bank_id:
2233- return [bank_id]
2234- return []
2235+ _data_editor_class = CheckDataEditor
2236
2237 def _setup_widgets(self):
2238 printer = get_current_cheque_printer_settings(self.conn)
2239@@ -676,52 +600,14 @@
2240 else:
2241 banks = printer.get_banks()
2242 items = [("%s - %s" % (code, bank.name), code)
2243- for code, bank in banks.items()]
2244+ for code, bank in banks.items()]
2245 self.bank_combo.prefill(items)
2246 BasePaymentMethodSlave._setup_widgets(self)
2247
2248
2249-class BillMethodSlave(BasePaymentMethodSlave):
2250- _data_slave_class = BillDataSlave
2251-
2252- def __init__(self, wizard, parent, conn, sale, payment_method,
2253- outstanding_value=currency(0)):
2254- BasePaymentMethodSlave.__init__(self, wizard, parent, conn,
2255- sale, payment_method,
2256- outstanding_value=outstanding_value)
2257- self.bank_label.hide()
2258- self.bank_combo.hide()
2259-
2260- def get_slave_by_adapted_payment(self, adapted_payment):
2261- payment = adapted_payment.get_adapted()
2262- return self.get_payment_slave(payment)
2263-
2264-
2265-class _MoneyData(BillDataSlave):
2266-
2267- def create_model(self, conn):
2268- money_method = PaymentMethod.get_by_name(conn, 'money')
2269- apayment = money_method.create_payment(self._method_iface,
2270- self._payment_group,
2271- self._value,
2272- self._due_date)
2273- return apayment.get_adapted()
2274-
2275-
2276-class _StoreCreditData(BillDataSlave):
2277-
2278- def create_model(self, conn):
2279- method = PaymentMethod.get_by_name(conn, 'store_credit')
2280- apayment = method.create_payment(self._method_iface,
2281- self._payment_group,
2282- self._value,
2283- self._due_date)
2284- return apayment.get_adapted()
2285-
2286-
2287 class MoneyMethodSlave(BasePaymentMethodSlave):
2288- model_type = _TemporaryMoneyData
2289- _data_slave_class = _MoneyData
2290+ model_type = _BaseTemporaryMethodData
2291+ _data_editor_class = BasePaymentDataEditor
2292
2293 def __init__(self, wizard, parent, conn, total_amount,
2294 payment_method, outstanding_value=currency(0)):
2295@@ -732,17 +618,16 @@
2296 self.bank_combo.hide()
2297 self.first_duedate_lbl.hide()
2298 self.first_duedate.hide()
2299-
2300- def get_slave_by_adapted_payment(self, adapted_payment):
2301- return self.get_payment_slave(adapted_payment.get_adapted())
2302-
2303- def create_model(self, conn):
2304- return _TemporaryMoneyData()
2305+ self.intervals_lbl.hide()
2306+ self.intervals.hide()
2307+ self.interval_type_combo.hide()
2308+ self.installments_number_lbl.hide()
2309+ self.installments_number.hide()
2310
2311
2312 class StoreCreditMethodSlave(BasePaymentMethodSlave):
2313- model_type = _TemporaryStoreCreditData
2314- _data_slave_class = _StoreCreditData
2315+ model_type = _BaseTemporaryMethodData
2316+ _data_editor_class = BasePaymentDataEditor
2317
2318 def __init__(self, wizard, parent, conn, total_amount,
2319 payment_method, outstanding_value=currency(0)):
2320@@ -754,21 +639,15 @@
2321 self.first_duedate_lbl.hide()
2322 self.first_duedate.hide()
2323
2324- def get_slave_by_adapted_payment(self, adapted_payment):
2325- return self.get_payment_slave(adapted_payment.get_adapted())
2326-
2327- def create_model(self, conn):
2328- return _TemporaryStoreCreditData()
2329-
2330
2331 class CardMethodSlave(BaseEditorSlave):
2332 """A base payment method slave for card and finance methods.
2333 Available slaves are: CardMethodSlave
2334 """
2335+
2336 gladefile = 'CreditProviderMethodSlave'
2337 model_type = _TemporaryCreditProviderGroupData
2338- proxy_widgets = ('credit_provider',
2339- 'installments_number')
2340+ proxy_widgets = ('credit_provider', 'installments_number')
2341
2342 def __init__(self, wizard, parent, conn, order, payment_method,
2343 outstanding_value=currency(0)):
2344@@ -817,9 +696,7 @@
2345 raise ValueError('You must have credit providers information '
2346 'stored in the database before start doing '
2347 'sales')
2348- return _TemporaryCreditProviderGroupData(
2349- group=self._payment_group,
2350- provider=None)
2351+ return _TemporaryCreditProviderGroupData(provider=None)
2352
2353 # Private
2354
2355@@ -920,15 +797,16 @@
2356 max_installments = self.installments_number.get_range()[1]
2357 min_installments = self.installments_number.get_range()[0]
2358 if not min_installments <= installments <= max_installments:
2359- return ValidationError(_(u'Number of installments must be greater'
2360- ' than %d and lower than %d')
2361- % (min_installments, max_installments))
2362+ return ValidationError(_(u'Number of installments must be greater '
2363+ 'than %d and lower than %d')
2364+ % (min_installments, max_installments))
2365
2366
2367 class _MultipleMethodEditor(BaseEditor):
2368 """A generic editor that attaches a payment method slave in a toplevel
2369 window.
2370 """
2371+
2372 gladefile = 'HolderTemplate'
2373 model_type = PaymentGroup
2374 model_name = _(u'Payment')
2375
2376=== modified file 'stoqlib/gui/wizards/purchasewizard.py'
2377--- stoqlib/gui/wizards/purchasewizard.py 2011-01-07 16:03:44 +0000
2378+++ stoqlib/gui/wizards/purchasewizard.py 2011-02-01 20:17:04 +0000
2379@@ -324,8 +324,6 @@
2380 gladefile = 'PurchasePaymentStep'
2381 model_type = PaymentGroup
2382 payment_widgets = ('method_combo',)
2383- order_widgets = ('subtotal_lbl',
2384- 'total_lbl')
2385
2386 def __init__(self, wizard, previous, conn, model,
2387 outstanding_value=currency(0)):
2388@@ -374,32 +372,28 @@
2389 if not self.slave:
2390 self._set_method_slave()
2391
2392- def _update_totals(self, *args):
2393- for field_name in ['purchase_subtotal', 'purchase_total']:
2394- self.order_proxy.update(field_name)
2395-
2396 #
2397 # WizardStep hooks
2398 #
2399
2400+ def validate_step(self):
2401+ return self.slave.finish()
2402+
2403 def next_step(self):
2404 return FinishPurchaseStep(self.wizard, self, self.conn,
2405 self.order)
2406
2407 def post_init(self):
2408+ self.model.clear_unused()
2409 self.method_combo.grab_focus()
2410 self.main_box.set_focus_chain([self.payment_method_hbox,
2411 self.method_slave_holder])
2412 self.payment_method_hbox.set_focus_chain([self.method_combo])
2413 self.register_validate_function(self.wizard.refresh_next)
2414 self.force_validation()
2415- can_finish = self.slave.payment_list.get_total_difference() == 0
2416- self.wizard.refresh_next(can_finish)
2417
2418 def setup_proxies(self):
2419 self._setup_widgets()
2420- self.order_proxy = self.add_proxy(self.order,
2421- PurchasePaymentStep.order_widgets)
2422 self.proxy = self.add_proxy(self.model,
2423 PurchasePaymentStep.payment_widgets)
2424 # Set the first payment method as default
2425
2426=== modified file 'stoqlib/lib/defaults.py'
2427--- stoqlib/lib/defaults.py 2010-12-09 19:20:41 +0000
2428+++ stoqlib/lib/defaults.py 2011-02-01 20:17:04 +0000
2429@@ -61,10 +61,10 @@
2430 INTERVALTYPE_MONTH: _('Months'),
2431 INTERVALTYPE_YEAR: _('Years')}
2432
2433-interval_values = {INTERVALTYPE_DAY: 1,
2434- INTERVALTYPE_WEEK: 7,
2435- INTERVALTYPE_MONTH: 30,
2436- INTERVALTYPE_YEAR: 365}
2437+interval_name = {INTERVALTYPE_DAY: 'days',
2438+ INTERVALTYPE_WEEK: 'weeks',
2439+ INTERVALTYPE_MONTH: 'months',
2440+ INTERVALTYPE_YEAR: 'years'}
2441
2442 # weekday constants
2443
2444@@ -86,31 +86,26 @@
2445 return _libc
2446
2447
2448-def calculate_interval(interval_type, intervals):
2449- """Get the interval type value for a certain INTERVALTYPE_* constant.
2450- Intervals are useful modes to calculate payment duedates.
2451-
2452- @param interval_type:
2453- @param intervals:
2454- @returns:
2455-
2456- >>> calculate_interval(INTERVALTYPE_DAY, 5)
2457- 5
2458-
2459- >>> calculate_interval(INTERVALTYPE_MONTH, 3)
2460- 90
2461-
2462- >>> calculate_interval(INTERVALTYPE_YEAR, 10)
2463- 3650
2464-
2465+def calculate_delta_interval(interval_type, intervals):
2466+ """Get the relativedelta value for a certain INTERVALTYPE_* constant.
2467+
2468+ Intervals are useful modes to calculate payment duedates, just sum
2469+ them with a L{datetime.datetime} or L{datetime.date} object.
2470+
2471+ @param interval_type: one of the INTERVALTYPE_* above
2472+ @param intervals: an int representing the number of intervals
2473+ @returns: a L{relativedelta.relativedelta} object
2474 """
2475- if not interval_values.has_key(interval_type):
2476+ if not interval_types.has_key(interval_type):
2477 raise KeyError('Invalid interval_type %r argument for '
2478 'calculate_interval function.' % (interval_type,))
2479 if not type(intervals) == int:
2480 raise TypeError('Invalid type for intervals argument. It must be '
2481 'integer, got %s' % type(intervals))
2482- return interval_values[interval_type] * intervals
2483+
2484+ kargs = {interval_name[interval_type]: intervals}
2485+
2486+ return relativedelta.relativedelta(**kargs)
2487
2488
2489 def get_weekday_start():
2490
2491=== renamed file 'stoqlib/lib/paymentoperation.py' => 'stoqlib/lib/payment.py'
2492--- stoqlib/lib/paymentoperation.py 2009-02-11 13:56:34 +0000
2493+++ stoqlib/lib/payment.py 2011-02-01 20:17:04 +0000
2494@@ -20,10 +20,15 @@
2495 ## Foundation, Inc., or visit: http://www.gnu.org/.
2496 ##
2497 ## Author(s): Johan Dahlin <jdahlin@async.com.br>
2498+## Thiago Bellini <hackedbellini@async.com.br>
2499 ##
2500
2501+import datetime
2502+from decimal import Decimal
2503+
2504 from zope.interface import implements
2505
2506+from stoqlib.lib.defaults import calculate_delta_interval, quantize
2507 from stoqlib.lib.interfaces import IPaymentOperation, IPaymentOperationManager
2508 from stoqlib.lib.translation import stoqlib_gettext
2509
2510@@ -52,3 +57,59 @@
2511 def get(self, name):
2512 return self._methods.get(name)
2513
2514+
2515+def generate_payments_values(value, installments_number,
2516+ interest=Decimal(0)):
2517+ """Calculates the values of payments
2518+
2519+ @param value: value of payment
2520+ @param installments_number: the number of installments
2521+ @param interest: a L{Decimal} with the interest
2522+ @returns: a list with the values
2523+ """
2524+ assert installments_number > 0
2525+
2526+ if interest:
2527+ interest_rate = interest / 100 + 1
2528+ normalized_value = quantize((value / installments_number)
2529+ * interest_rate)
2530+ interest_total = normalized_value * installments_number - value
2531+ else:
2532+ normalized_value = quantize(value / installments_number)
2533+ interest_total = Decimal(0)
2534+
2535+ payments = []
2536+ payments_total = Decimal(0)
2537+ for i in range(installments_number):
2538+ payments.append(normalized_value)
2539+ payments_total += normalized_value
2540+
2541+ # Adjust the last payment so the total will sum up nicely.
2542+ difference = -(payments_total - interest_total - value)
2543+ if difference:
2544+ payments[-1] += difference
2545+
2546+ return payments
2547+
2548+
2549+def generate_payments_due_dates(installments_number, first_due_date,
2550+ interval, interval_type):
2551+ """Calculates the due dates of payments
2552+
2553+ @param installments_number: the number of installments
2554+ @param first_due_date: a L{datetime.datetime} or L{datetime.date}
2555+ object containing the first due date
2556+ @param interval: the interval between due_dates
2557+ @param interval_type: an interval_type from L{stoqlib.lib.defaults}
2558+ @returns: a list with the due_dates
2559+ """
2560+ assert installments_number > 0
2561+
2562+ due_dates = []
2563+ delta = calculate_delta_interval(interval_type, interval)
2564+ d = first_due_date
2565+ for i in range(installments_number):
2566+ due_dates.append(d)
2567+ d += delta
2568+
2569+ return due_dates
2570
2571=== added file 'stoqlib/lib/test/test_payment.py'
2572--- stoqlib/lib/test/test_payment.py 1970-01-01 00:00:00 +0000
2573+++ stoqlib/lib/test/test_payment.py 2011-02-01 20:17:04 +0000
2574@@ -0,0 +1,130 @@
2575+# -*- coding: utf-8 -*-
2576+# vi:si:et:sw=4:sts=4:ts=4
2577+
2578+##
2579+## Copyright (C) 2008 Async Open Source <http://www.async.com.br>
2580+## All rights reserved
2581+##
2582+## This program is free software; you can redistribute it and/or modify
2583+## it under the terms of the GNU Lesser General Public License as published by
2584+## the Free Software Foundation; either version 2 of the License, or
2585+## (at your option) any later version.
2586+##
2587+## This program is distributed in the hope that it will be useful,
2588+## but WITHOUT ANY WARRANTY; without even the implied warranty of
2589+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2590+## GNU General Public License for more details.
2591+##
2592+## You should have received a copy of the GNU Lesser General Public License
2593+## along with this program; if not, write to the Free Software
2594+## Foundation, Inc., or visit: http://www.gnu.org/.
2595+##
2596+## Author(s): Thiago Bellini <hackedbellini@async.com.br>
2597+##
2598+""" Test for stoqlib/lib/payment.py module. """
2599+
2600+import datetime
2601+from decimal import Decimal
2602+
2603+from stoqlib.domain.test.domaintest import DomainTest
2604+from stoqlib.lib.defaults import (INTERVALTYPE_YEAR, INTERVALTYPE_MONTH,
2605+ INTERVALTYPE_WEEK, INTERVALTYPE_DAY)
2606+from stoqlib.lib.payment import (generate_payments_due_dates,
2607+ generate_payments_values)
2608+
2609+
2610+
2611+class TestPaymentFunctions(DomainTest):
2612+ """"A class for testing the functions on lib/payment.py
2613+ """
2614+
2615+ def testGeneratePaymentsDueDates(self):
2616+ # Test 1
2617+ due_date = datetime.date(year=2010, month=4, day=1)
2618+ due_dates = generate_payments_due_dates(5, due_date, 1,
2619+ INTERVALTYPE_MONTH)
2620+ expected = [datetime.date(2010, 4, 1),
2621+ datetime.date(2010, 5, 1),
2622+ datetime.date(2010, 6, 1),
2623+ datetime.date(2010, 7, 1),
2624+ datetime.date(2010, 8, 1)]
2625+ self.assertEqual(due_dates, expected)
2626+ self.assertEqual(len(due_dates), 5)
2627+ self.assertEqual(due_dates[0], due_date)
2628+ for t in due_dates[1:]:
2629+ self.failUnless(t > due_date)
2630+
2631+ # Test 2
2632+ due_date = datetime.date(year=2010, month=1, day=31)
2633+ due_dates = generate_payments_due_dates(10, due_date, 2,
2634+ INTERVALTYPE_WEEK)
2635+ expected = [datetime.date(2010, 1, 31),
2636+ datetime.date(2010, 2, 14),
2637+ datetime.date(2010, 2, 28),
2638+ datetime.date(2010, 3, 14),
2639+ datetime.date(2010, 3, 28),
2640+ datetime.date(2010, 4, 11),
2641+ datetime.date(2010, 4, 25),
2642+ datetime.date(2010, 5, 9),
2643+ datetime.date(2010, 5, 23),
2644+ datetime.date(2010, 6, 6)]
2645+ self.assertEqual(due_dates, expected)
2646+ self.assertEqual(len(due_dates), 10)
2647+ self.assertEqual(due_dates[0], due_date)
2648+ for t in due_dates[1:]:
2649+ self.failUnless(t > due_date)
2650+
2651+ # Test 3
2652+ due_date = due_date = datetime.date(year=2011, month=3, day=14)
2653+ due_dates = generate_payments_due_dates(3, due_date, 10,
2654+ INTERVALTYPE_DAY)
2655+ expected = [datetime.date(2011, 3, 14),
2656+ datetime.date(2011, 3, 24),
2657+ datetime.date(2011, 4, 3)]
2658+ self.assertEqual(due_dates, expected)
2659+ self.assertEqual(len(due_dates), 3)
2660+ self.assertEqual(due_dates[0], due_date)
2661+ for t in due_dates[1:]:
2662+ self.failUnless(t > due_date)
2663+
2664+ # Test 4
2665+ due_date = due_date = datetime.date(year=2012, month=12, day=31)
2666+ due_dates = generate_payments_due_dates(4, due_date, 2,
2667+ INTERVALTYPE_YEAR)
2668+ expected = [datetime.date(2012, 12, 31),
2669+ datetime.date(2014, 12, 31),
2670+ datetime.date(2016, 12, 31),
2671+ datetime.date(2018, 12, 31)]
2672+ self.assertEqual(due_dates, expected)
2673+ self.assertEqual(len(due_dates), 4)
2674+ self.assertEqual(due_dates[0], due_date)
2675+ for t in due_dates[1:]:
2676+ self.failUnless(t > due_date)
2677+
2678+ # Test 5
2679+ due_date = due_date = datetime.date(year=2010, month=1, day=1)
2680+ self.assertRaises(AssertionError, generate_payments_due_dates, 0,
2681+ due_date, 1, INTERVALTYPE_YEAR)
2682+
2683+ def testGeneratePaymentsValues(self):
2684+ # Test 1
2685+ values = generate_payments_values(Decimal(101), 3)
2686+ expected = [Decimal('33.67'), Decimal('33.67'), Decimal('33.66')]
2687+ self.assertEqual(values, expected)
2688+ self.assertEqual(len(values), 3)
2689+
2690+ self.assertEqual(sum(values), Decimal(101))
2691+
2692+ # Test 2
2693+ values = generate_payments_values(Decimal('10.5'), 5,
2694+ Decimal('1'))
2695+ expected = [Decimal('2.12'), Decimal('2.12'), Decimal('2.12'),
2696+ Decimal('2.12'), Decimal('2.12')]
2697+ self.assertEqual(values, expected)
2698+ self.assertEqual(len(values), 5)
2699+
2700+ self.assertEqual(sum(values), (Decimal('10.5') + Decimal('0.10')))
2701+
2702+ # Test 3
2703+ self.assertRaises(AssertionError, generate_payments_values,
2704+ Decimal('2'), 0)

Subscribers

People subscribed via source and target branches