Merge lp:~bellini666/stoqlib/closed_product into lp:~stoq-dev/stoqlib/master
- closed_product
- Merge into master
Status: | Merged |
---|---|
Merged at revision: | 3331 |
Proposed branch: | lp:~bellini666/stoqlib/closed_product |
Merge into: | lp:~stoq-dev/stoqlib/master |
Diff against target: |
364 lines (+206/-23) 7 files modified
external/sqlobject/viewable.py (+1/-1) stoqlib/domain/sellable.py (+17/-0) stoqlib/domain/test/test_sellable.py (+52/-0) stoqlib/domain/views.py (+16/-3) stoqlib/gui/editors/sellableeditor.py (+59/-13) stoqlib/gui/search/productsearch.py (+49/-4) stoqlib/reporting/product.py (+12/-2) |
To merge this branch: | bzr merge lp:~bellini666/stoqlib/closed_product |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ronaldo Maia | Pending | ||
Review via email: mp+50754@code.launchpad.net |
Commit message
Description of the change
Opção de ter produtos inativos.
Correções para o #4304
- 3321. By Thiago Bellini
-
Quick Fix: Correcting a traceback on POS app when checkout payment method was 'check' or 'bill'
- 3322. By Thiago Bellini
-
Quick fix: Hope this will fix all problems when using a payment slave.
- 3323. By Gabriel Gerga
-
Exportar vendas a vista para NF-e. r=romaia
- 3324. By Ronaldo Maia
-
Viewable select results didn't include objects created inside a transaction,
even when selecting from that transaction.Fix a test afected by this
- 3325. By Ronaldo Maia
-
Update a few report tests pdfs
- 3326. By Thiago Bellini
-
Make the labels of the 'yesno' dialog buttons more useful.
- 3327. By Gabriel Gerga
-
Corrected values used in payments editor according freight, surcharge and discount. r=romaia
- 3328. By Thiago Bellini
-
Quick Fix: Forgot a closing ')' on sellableeditor. Sorry! :(
- 3329. By Ronaldo Maia
-
Adicionando suporte experimental para boletos bancários
- Importando Projeto Pyboleto (com algumas mudanças nosssas)
- Adicionando opção de imprimir boletos (no formato carnê) a partir dos
detalhes de uma venda - 3335. By Thiago Bellini
-
Removing the editor from Closed Products Search and code adjustments.
- 3336. By Thiago Bellini
-
Code corrections.
- 3337. By Thiago Bellini
-
Add tests for new methods and views.
- 3338. By Thiago Bellini
-
Code corrections.
- 3339. By Thiago Bellini
-
More code corrections.
Preview Diff
1 | === modified file 'external/sqlobject/viewable.py' |
2 | --- external/sqlobject/viewable.py 2011-02-28 15:29:21 +0000 |
3 | +++ external/sqlobject/viewable.py 2011-03-04 18:09:29 +0000 |
4 | @@ -149,7 +149,7 @@ |
5 | |
6 | setup_attributes(cls, new_attrs) |
7 | |
8 | - columns = new_attrs['columns'] |
9 | + columns = new_attrs.get('columns', getattr(cls, 'columns', None)) |
10 | if not columns: |
11 | return |
12 | |
13 | |
14 | === modified file 'stoqlib/domain/sellable.py' |
15 | --- stoqlib/domain/sellable.py 2011-02-08 14:06:58 +0000 |
16 | +++ stoqlib/domain/sellable.py 2011-03-04 18:09:29 +0000 |
17 | @@ -378,6 +378,10 @@ |
18 | |
19 | commission = property(_get_commission, _set_commission) |
20 | |
21 | + # |
22 | + # Accessors |
23 | + # |
24 | + |
25 | def can_be_sold(self): |
26 | """Whether the sellable is available and can be sold. |
27 | @returns: if the item can be sold |
28 | @@ -401,6 +405,19 @@ |
29 | raise ValueError('This sellable is already unavailable') |
30 | self.status = self.STATUS_UNAVAILABLE |
31 | |
32 | + def is_closed(self): |
33 | + """Whether the sellable is closed or not. |
34 | + |
35 | + @returns: True if closed, False otherwise. |
36 | + """ |
37 | + return self.status == Sellable.STATUS_CLOSED |
38 | + |
39 | + def close(self): |
40 | + """Mark the sellable as closed""" |
41 | + if self.is_closed(): |
42 | + raise ValueError('This sellable is already closed') |
43 | + self.status = Sellable.STATUS_CLOSED |
44 | + |
45 | def cancel(self): |
46 | """Cancel the sellable""" |
47 | if self.can_be_sold(): |
48 | |
49 | === modified file 'stoqlib/domain/test/test_sellable.py' |
50 | --- stoqlib/domain/test/test_sellable.py 2011-02-08 14:06:58 +0000 |
51 | +++ stoqlib/domain/test/test_sellable.py 2011-03-04 18:09:29 +0000 |
52 | @@ -29,6 +29,9 @@ |
53 | SellableCategory) |
54 | from stoqlib.domain.product import Product |
55 | from stoqlib.domain.test.domaintest import DomainTest |
56 | +from stoqlib.domain.views import (ProductFullStockView, |
57 | + ProductFullWithClosedStockView, |
58 | + ProductClosedStockView) |
59 | |
60 | |
61 | class TestSellableCategory(DomainTest): |
62 | @@ -230,3 +233,52 @@ |
63 | constant3 = self.create_sellable_tax_constant() |
64 | sellable.tax_constant = constant3 |
65 | self.assertEquals(sellable.get_tax_constant(), constant3) |
66 | + |
67 | + def testClose(self): |
68 | + results_not_closed = ProductFullStockView.select(connection=self.trans) |
69 | + results_with_closed = ProductFullWithClosedStockView.select( |
70 | + connection=self.trans) |
71 | + results_only_closed = ProductClosedStockView.select( |
72 | + connection=self.trans) |
73 | + # Count the already there results. ProductClosedStockView should |
74 | + # not have any. |
75 | + # obs. Using len(list(res)) instead of res.count() because of a bug |
76 | + # on sqlobject that returns wrong count() on that views. |
77 | + count_not_closed = len(list(results_not_closed)) |
78 | + count_with_closed = len(list(results_with_closed)) |
79 | + count_only_closed = len(list(results_only_closed)) |
80 | + self.assertEqual(count_only_closed, 0) |
81 | + |
82 | + # Here we create a sellable. It should show on |
83 | + # ProductFullStockView and ProductFullWithClosedStock View, |
84 | + # but not on ProductClosedStockView. |
85 | + sellable = self.create_sellable() |
86 | + self.assertEqual(len(list(results_not_closed)), count_not_closed + 1L) |
87 | + self.assertEqual(len(list(results_with_closed)), count_with_closed + 1L) |
88 | + self.assertEqual(len(list(results_only_closed)), count_only_closed) |
89 | + ids = [result.id for result in results_not_closed] |
90 | + self.failIf(sellable.id not in ids) |
91 | + ids = [result.id for result in results_with_closed] |
92 | + self.failIf(sellable.id not in ids) |
93 | + ids = [result.id for result in results_only_closed] |
94 | + self.failIf(sellable.id in ids) |
95 | + |
96 | + # Here we close that sellable. It should now show on |
97 | + # ProductClosedStockViewand ProductFullWithClosedStock View, |
98 | + # but not on ProductFullStockView. |
99 | + sellable.close() |
100 | + self.assertEquals(sellable.status, Sellable.STATUS_CLOSED) |
101 | + self.assertTrue(sellable.is_closed()) |
102 | + self.assertEqual(len(list(results_not_closed)), count_not_closed) |
103 | + self.assertEqual(len(list(results_with_closed)), count_with_closed + 1L) |
104 | + self.assertEqual(len(list(results_only_closed)), count_only_closed + 1L) |
105 | + ids = [result.id for result in results_not_closed] |
106 | + self.failIf(sellable.id in ids) |
107 | + ids = [result.id for result in results_with_closed] |
108 | + self.failIf(sellable.id not in ids) |
109 | + ids = [result.id for result in results_only_closed] |
110 | + self.failIf(sellable.id not in ids) |
111 | + |
112 | + # When trying to close an already closed sellable, it should |
113 | + # raise a ValueError. |
114 | + self.assertRaises(ValueError, sellable.close) |
115 | |
116 | === modified file 'stoqlib/domain/views.py' |
117 | --- stoqlib/domain/views.py 2011-02-15 10:21:17 +0000 |
118 | +++ stoqlib/domain/views.py 2011-03-04 18:09:29 +0000 |
119 | @@ -79,6 +79,8 @@ |
120 | ) |
121 | |
122 | joins = [ |
123 | + INNERJOINOn(None, BaseSellableInfo, |
124 | + BaseSellableInfo.q.id == Sellable.q.base_sellable_infoID), |
125 | # Tax Constant |
126 | LEFTJOINOn(None, SellableTaxConstant, |
127 | SellableTaxConstant.q.id == Sellable.q.tax_constantID), |
128 | @@ -96,9 +98,7 @@ |
129 | ProductAdaptToStorable.q.id), |
130 | ] |
131 | |
132 | - clause = AND( |
133 | - BaseSellableInfo.q.id == Sellable.q.base_sellable_infoID, |
134 | - ) |
135 | + clause = Sellable.q.status != Sellable.STATUS_CLOSED |
136 | |
137 | @classmethod |
138 | def select_by_branch(cls, query, branch, having=None, connection=None): |
139 | @@ -154,6 +154,19 @@ |
140 | return sellable.price |
141 | |
142 | |
143 | +class ProductFullWithClosedStockView(ProductFullStockView): |
144 | + """Stores information about products, showing the closed ones too. |
145 | + """ |
146 | + |
147 | + clause = None |
148 | + |
149 | +class ProductClosedStockView(ProductFullWithClosedStockView): |
150 | + """Stores information about products that were closed. |
151 | + """ |
152 | + |
153 | + clause = Sellable.q.status == Sellable.STATUS_CLOSED |
154 | + |
155 | + |
156 | class ProductComponentView(ProductFullStockView): |
157 | columns = ProductFullStockView.columns |
158 | clause = AND(ProductFullStockView.clause, |
159 | |
160 | === modified file 'stoqlib/gui/editors/sellableeditor.py' |
161 | --- stoqlib/gui/editors/sellableeditor.py 2011-03-02 15:16:10 +0000 |
162 | +++ stoqlib/gui/editors/sellableeditor.py 2011-03-04 18:09:29 +0000 |
163 | @@ -236,23 +236,34 @@ |
164 | code = u'%d' % self._sellable.id |
165 | self.code.update(code) |
166 | self.setup_widgets() |
167 | - if model and self._sellable.can_remove(): |
168 | - button = self.add_button('Remove', 'gtk-delete') |
169 | - button.connect('clicked', self._on_delete_button__activate) |
170 | |
171 | self.set_description( |
172 | self.model.sellable.base_sellable_info.description) |
173 | |
174 | - def _on_delete_button__activate(self, button): |
175 | - msg = _(u"This will delete '%s' from the database. Are you sure?" |
176 | - % self._sellable.get_description()) |
177 | - if not yesno(msg, gtk.RESPONSE_NO, _(u"Delete"), _(u"Don't Delete")): |
178 | - return |
179 | - |
180 | - self._sellable.remove() |
181 | - # We don't call self.confirm since it will call validate_confirm |
182 | - self.cancel() |
183 | - self.main_dialog.retval = True |
184 | + self._setup_delete_close_reopen_button() |
185 | + |
186 | + def _add_extra_button(self, label, stock=None, |
187 | + callback_func=None, connect_on='clicked'): |
188 | + button = self.add_button(label, stock) |
189 | + if callback_func: |
190 | + button.connect(connect_on, callback_func, label) |
191 | + |
192 | + def _setup_delete_close_reopen_button(self): |
193 | + if self.model and self._sellable.can_remove(): |
194 | + self._add_extra_button(_('Remove'), 'gtk-delete', |
195 | + self._on_delete_button__clicked) |
196 | + elif self.model and self._sellable.is_unavailable(): |
197 | + label = (self._sellable.product and |
198 | + _('Close Product') or _('Close Service')) |
199 | + |
200 | + self._add_extra_button(label, None, |
201 | + self._on_close_sellable_button__clicked) |
202 | + elif self.model and self._sellable.is_closed(): |
203 | + label = (self._sellable.product and |
204 | + _('Reopen Product') or _('Reopen Service')) |
205 | + |
206 | + self._add_extra_button(label, None, |
207 | + self._on_reopen_sellable_button__clicked) |
208 | |
209 | def add_extra_tab(self, tabname, tabslave): |
210 | self.sellable_notebook.set_show_tabs(True) |
211 | @@ -402,6 +413,41 @@ |
212 | # Kiwi handlers |
213 | # |
214 | |
215 | + def _on_delete_button__clicked(self, button, parent_button_label=None): |
216 | + msg = _(u"This will delete '%s' from the database. Are you sure?" |
217 | + % self._sellable.get_description()) |
218 | + if not yesno(msg, gtk.RESPONSE_NO, _(u"Delete"), _(u"Don't Delete")): |
219 | + return |
220 | + |
221 | + self._sellable.remove() |
222 | + # We don't call self.confirm since it will call validate_confirm |
223 | + self.cancel() |
224 | + self.main_dialog.retval = True |
225 | + |
226 | + def _on_close_sellable_button__clicked(self, button, |
227 | + parent_button_label=None): |
228 | + msg = _(u"Do you really want to close '%s'?\n" |
229 | + u"Please note that when it's closed, you won't be able to " |
230 | + u"commercialize it anymore." % self._sellable.get_description()) |
231 | + if not yesno(msg, gtk.RESPONSE_NO, |
232 | + parent_button_label, _(u"Don't Close")): |
233 | + return |
234 | + |
235 | + self._sellable.close() |
236 | + self.confirm() |
237 | + |
238 | + def _on_reopen_sellable_button__clicked(self, button, |
239 | + parent_button_label=None): |
240 | + msg = _(u"Do you really want to reopen '%s'?\n" |
241 | + u"Note that when it's opened, you will be able to " |
242 | + u"commercialize it again." % self._sellable.get_description()) |
243 | + if not yesno(msg, gtk.RESPONSE_NO, |
244 | + parent_button_label, _(u"Keep Closed")): |
245 | + return |
246 | + |
247 | + self._sellable.set_unavailable() |
248 | + self.confirm() |
249 | + |
250 | def on_tax_constant__changed(self, combo): |
251 | self._update_tax_value() |
252 | |
253 | |
254 | === modified file 'stoqlib/gui/search/productsearch.py' |
255 | --- stoqlib/gui/search/productsearch.py 2011-02-15 10:21:17 +0000 |
256 | +++ stoqlib/gui/search/productsearch.py 2011-03-04 18:09:29 +0000 |
257 | @@ -37,7 +37,9 @@ |
258 | from stoqlib.domain.product import Product |
259 | from stoqlib.domain.sellable import Sellable |
260 | from stoqlib.domain.views import (ProductFullStockView, ProductQuantityView, |
261 | - ProductFullStockItemView, SoldItemView) |
262 | + ProductFullStockItemView, SoldItemView, |
263 | + ProductFullWithClosedStockView, |
264 | + ProductClosedStockView) |
265 | from stoqlib.gui.base.dialogs import run_dialog |
266 | from stoqlib.gui.base.gtkadds import change_button_appearance |
267 | from stoqlib.gui.base.search import (SearchDialog, SearchEditor, |
268 | @@ -50,8 +52,8 @@ |
269 | from stoqlib.lib.translation import stoqlib_gettext |
270 | from stoqlib.lib.validators import format_quantity, get_formatted_cost |
271 | from stoqlib.reporting.product import (ProductReport, ProductQuantityReport, |
272 | - ProductPriceReport, |
273 | - ProductStockReport, |
274 | + ProductClosedStockReport, |
275 | + ProductPriceReport, ProductStockReport, |
276 | ProductsSoldReport) |
277 | |
278 | _ = stoqlib_gettext |
279 | @@ -61,7 +63,7 @@ |
280 | title = _('Product Search') |
281 | table = Product |
282 | size = (775, 450) |
283 | - search_table = ProductFullStockView |
284 | + search_table = ProductFullWithClosedStockView |
285 | editor_class = ProductEditor |
286 | footer_ok_label = _('Add products') |
287 | searchbar_result_strings = (_('product'), _('products')) |
288 | @@ -413,3 +415,46 @@ |
289 | if branch is not None: |
290 | branch = PersonAdaptToBranch.get(branch, connection=conn) |
291 | return self.table.select_by_branch(query, branch, connection=conn) |
292 | + |
293 | + |
294 | +class ProductClosedStockSearch(ProductSearch): |
295 | + """A SearchEditor for Closed Products""" |
296 | + |
297 | + title = _('Closed Product Stock Search') |
298 | + table = search_table = ProductClosedStockView |
299 | + has_new_button = False |
300 | + |
301 | + def __init__(self, conn, hide_footer=True, hide_toolbar=True, |
302 | + selection_mode=gtk.SELECTION_BROWSE, |
303 | + hide_cost_column=True, use_product_statuses=None, |
304 | + hide_price_column=True): |
305 | + ProductSearch.__init__(self, conn, hide_footer, hide_toolbar, |
306 | + selection_mode, hide_cost_column, |
307 | + use_product_statuses, hide_price_column) |
308 | + |
309 | + def create_filters(self): |
310 | + self.set_text_field_columns(['description', 'barcode', |
311 | + 'category_description']) |
312 | + self.executer.set_query(self.executer_query) |
313 | + |
314 | + # Branch |
315 | + branch_filter = self.create_branch_filter(_('In branch:')) |
316 | + branch_filter.select(None) |
317 | + self.add_filter(branch_filter, columns=[]) |
318 | + self.branch_filter = branch_filter |
319 | + |
320 | + def _setup_print_slave(self): |
321 | + pass |
322 | + |
323 | + def _has_rows(self, results, obj): |
324 | + SearchEditor._has_rows(self, results, obj) |
325 | + |
326 | + # |
327 | + # SearchDialog Hooks |
328 | + # |
329 | + |
330 | + def on_print_button_clicked(self, widget): |
331 | + print_report(ProductClosedStockReport, self.results, |
332 | + filters=self.search.get_search_filters(), |
333 | + branch_name=self.branch_filter.combo.get_active_text()) |
334 | + |
335 | |
336 | === modified file 'stoqlib/reporting/product.py' |
337 | --- stoqlib/reporting/product.py 2011-02-15 10:21:17 +0000 |
338 | +++ stoqlib/reporting/product.py 2011-03-04 18:09:29 +0000 |
339 | @@ -32,9 +32,9 @@ |
340 | from stoqlib.lib.validators import format_quantity, get_formatted_price |
341 | from stoqlib.lib.translation import stoqlib_gettext as _ |
342 | from stoqlib.domain.product import ProductHistory |
343 | -from stoqlib.domain.views import (ProductFullStockView, |
344 | +from stoqlib.domain.views import (SoldItemView, ProductFullStockView, |
345 | ProductFullStockItemView, |
346 | - SoldItemView) |
347 | + ProductClosedStockView) |
348 | |
349 | |
350 | class ProductReport(ObjectListReport): |
351 | @@ -237,3 +237,13 @@ |
352 | |
353 | self.add_object_table(self._stock_products, self.get_columns(), |
354 | summary_row=self.get_summary_row()) |
355 | + |
356 | + |
357 | +class ProductClosedStockReport(ProductStockReport): |
358 | + report_name = _("Closed Product Stock Report") |
359 | + main_object_name = _("closed products") |
360 | + obj_type = ProductClosedStockView |
361 | + |
362 | + def _setup_table(self): |
363 | + self.add_object_table(self._stock_products, self.get_columns(), |
364 | + summary_row=self.get_summary_row()) |