[POS-commit] r8188 - in stoqlib/trunk: data/glade data/sql stoqlib/database stoqlib/domain stoqlib/domain/test stoqlib/gui/editors stoqlib/gui/wizards
george at async.com.br
george at async.com.br
Fri Jul 31 11:01:36 BRT 2009
Author: george
Date: Fri Jul 31 11:01:36 2009
New Revision: 8188
Log:
# Wizard de abertura de producao (stoqlib). r=romaia
Added:
stoqlib/trunk/data/glade/FinishOpenProductionOrderStep.glade
stoqlib/trunk/data/glade/OpenProductionOrderStep.glade
stoqlib/trunk/data/glade/ProductionItemEditor.glade
stoqlib/trunk/data/sql/patch-01-25.sql
stoqlib/trunk/stoqlib/domain/test/test_production.py
stoqlib/trunk/stoqlib/gui/editors/productioneditor.py
stoqlib/trunk/stoqlib/gui/wizards/productionwizard.py
Modified:
stoqlib/trunk/data/glade/AbstractItemStep.glade
stoqlib/trunk/stoqlib/database/tables.py
stoqlib/trunk/stoqlib/domain/exampledata.py
stoqlib/trunk/stoqlib/domain/product.py
stoqlib/trunk/stoqlib/domain/production.py
stoqlib/trunk/stoqlib/domain/service.py
stoqlib/trunk/stoqlib/domain/test/test_product.py
stoqlib/trunk/stoqlib/domain/views.py
stoqlib/trunk/stoqlib/gui/wizards/abstractwizard.py
Modified: stoqlib/trunk/data/glade/AbstractItemStep.glade
==============================================================================
--- stoqlib/trunk/data/glade/AbstractItemStep.glade Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/data/glade/AbstractItemStep.glade Fri Jul 31 11:01:36 2009 (r8188)
@@ -14,12 +14,12 @@
<property name="spacing">5</property>
<property name="visible">True</property>
<child>
- <widget class="ProxyLabel" id="kiwilabel1">
+ <widget class="ProxyLabel" id="item_lbl">
<property name="can_focus">True</property>
<property name="data_type">unicode</property>
<property name="is_focus">True</property>
<property name="label" context="yes" translatable="yes">Item:</property>
- <property name="model_attribute">kiwilabel1</property>
+ <property name="model_attribute">item_lbl</property>
<property name="selectable">True</property>
<property name="visible">True</property>
<property name="xalign">1.0</property>
@@ -112,7 +112,7 @@
<property name="digits">2</property>
<property name="is_focus">True</property>
<property name="model_attribute">cost</property>
- <property name="text" context="yes" translatable="yes">0.0</property>
+ <property name="text" context="yes" translatable="yes">0.00</property>
<property name="visible">True</property>
</widget>
<packing>
@@ -158,9 +158,6 @@
</widget>
</child>
</widget>
- <packing>
- <property name="expand">False</property>
- </packing>
</child>
<child>
<widget class="GtkButton" id="product_button">
@@ -198,7 +195,6 @@
</child>
</widget>
<packing>
- <property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
Added: stoqlib/trunk/data/glade/FinishOpenProductionOrderStep.glade
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stoqlib/trunk/data/glade/FinishOpenProductionOrderStep.glade Fri Jul 31 11:01:36 2009 (r8188)
@@ -0,0 +1,70 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
+<glade-interface domain="stoqlib">
+ <widget class="GtkWindow" id="FinishOpenProductionOrderStep">
+ <property name="default_height">250</property>
+ <property name="default_width">440</property>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="label" context="yes" translatable="yes"><b>Listing all the components needed by the production:</b></property>
+ <property name="use_markup">True</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.0</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ObjectList" id="materials">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="spacing">6</property>
+ <property name="visible">True</property>
+ <child>
+ <widget class="ProxyButton" id="edit_button">
+ <property name="data_type">str</property>
+ <property name="label">gtk-edit</property>
+ <property name="model_attribute">edit_button</property>
+ <property name="use_stock">True</property>
+ <property name="use_underline">False</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyCheckButton" id="start_production_check">
+ <property name="data_type">bool</property>
+ <property name="label" context="yes" translatable="yes">Start Production even if there are missing components</property>
+ <property name="model_attribute">start_production_check</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Added: stoqlib/trunk/data/glade/OpenProductionOrderStep.glade
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stoqlib/trunk/data/glade/OpenProductionOrderStep.glade Fri Jul 31 11:01:36 2009 (r8188)
@@ -0,0 +1,201 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
+<glade-interface domain="stoqlib">
+ <widget class="GtkWindow" id="OpenProductionOrderStep">
+ <property name="default_height">250</property>
+ <property name="default_width">440</property>
+ <child>
+ <widget class="GtkTable" id="table">
+ <property name="border_width">12</property>
+ <property name="column_spacing">6</property>
+ <property name="n_columns">4</property>
+ <property name="n_rows">4</property>
+ <property name="row_spacing">6</property>
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="label" context="yes" translatable="yes">Open Date:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="label" context="yes" translatable="yes">Branch:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="label" context="yes" translatable="yes">Responsible:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="label" context="yes" translatable="yes">Description:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">4</property>
+ <property name="top_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyDateEntry" id="open_date">
+ <property name="data_type">date</property>
+ <property name="mandatory">True</property>
+ <property name="model_attribute">open_date</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyComboEntry" id="branch">
+ <property name="data_type">object</property>
+ <property name="mandatory">True</property>
+ <property name="model_attribute">branch</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyComboEntry" id="responsible">
+ <property name="data_type">object</property>
+ <property name="mandatory">True</property>
+ <property name="model_attribute">responsible</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">3</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyEntry" id="description">
+ <property name="data_type">unicode</property>
+ <property name="mandatory">True</property>
+ <property name="model_attribute">description</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">4</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="label" context="yes" translatable="yes">Number:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="label" context="yes" translatable="yes">Expected Start Date:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyLabel" id="order_number">
+ <property name="data_type">unicode</property>
+ <property name="model_attribute">order_number</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyDateEntry" id="expected_start_date">
+ <property name="data_type">date</property>
+ <property name="model_attribute">expected_start_date</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Added: stoqlib/trunk/data/glade/ProductionItemEditor.glade
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stoqlib/trunk/data/glade/ProductionItemEditor.glade Fri Jul 31 11:01:36 2009 (r8188)
@@ -0,0 +1,170 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
+<glade-interface domain="stoqlib">
+ <widget class="GtkWindow" id="ProductionItemEditor">
+ <property name="default_height">250</property>
+ <property name="default_width">440</property>
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="column_spacing">6</property>
+ <property name="n_columns">4</property>
+ <property name="n_rows">3</property>
+ <property name="row_spacing">6</property>
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="label" context="yes" translatable="yes">Production:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="label" context="yes" translatable="yes">Description:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="quantity_lbl">
+ <property name="label" context="yes" translatable="yes">Quantity:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyLabel" id="order_number">
+ <property name="data_type">unicode</property>
+ <property name="model_attribute">order_number</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyLabel" id="description">
+ <property name="data_type">unicode</property>
+ <property name="model_attribute">description</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxySpinButton" id="quantity">
+ <property name="adjustment">1 0 100 1 10 0</property>
+ <property name="climb_rate">1.0</property>
+ <property name="data_type">Decimal</property>
+ <property name="digits">2</property>
+ <property name="is_focus">True</property>
+ <property name="model_attribute">quantity</property>
+ <property name="text" context="yes" translatable="yes">1.00</property>
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">3</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="location_content">
+ <property name="label" context="yes" translatable="yes">Location:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="label" context="yes" translatable="yes">Unit:</property>
+ <property name="visible">True</property>
+ <property name="xalign">1.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">3</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyLabel" id="location">
+ <property name="data_type">unicode</property>
+ <property name="model_attribute">location</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">2</property>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="ProxyLabel" id="unit_description">
+ <property name="data_type">unicode</property>
+ <property name="model_attribute">unit_description</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.0</property>
+ </widget>
+ <packing>
+ <property name="bottom_attach">3</property>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
Added: stoqlib/trunk/data/sql/patch-01-25.sql
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stoqlib/trunk/data/sql/patch-01-25.sql Fri Jul 31 11:01:36 2009 (r8188)
@@ -0,0 +1,42 @@
+-- # 4037: Wizard de abertura de produção
+
+ALTER TABLE production_order ADD COLUMN expected_start_date timestamp;
+ALTER TABLE production_order ADD COLUMN start_date timestamp;
+
+CREATE TABLE production_item (
+ id serial NOT NULL PRIMARY KEY,
+ te_created_id bigint UNIQUE REFERENCES transaction_entry(id),
+ te_modified_id bigint UNIQUE REFERENCES transaction_entry(id),
+
+ quantity numeric(10, 2) CONSTRAINT positive_quantity CHECK (quantity >= 0),
+ product_id bigint REFERENCES product(id),
+ order_id bigint REFERENCES production_order(id)
+);
+
+
+CREATE TABLE production_material (
+ id serial NOT NULL PRIMARY KEY,
+ te_created_id bigint UNIQUE REFERENCES transaction_entry(id),
+ te_modified_id bigint UNIQUE REFERENCES transaction_entry(id),
+
+ needed numeric(10, 2) CONSTRAINT positive_quantity CHECK (needed > 0),
+ consumed numeric(10, 2) DEFAULT 0 CONSTRAINT positive_consumed CHECK (consumed >= 0),
+ allocated numeric(10, 2) DEFAULT 0 CONSTRAINT positive_allocated CHECK (allocated >= 0),
+ to_purchase numeric(10, 2) CONSTRAINT positive_purchase_quantity CHECK (to_purchase >= 0),
+ to_make numeric(10, 2) CONSTRAINT positive_make_quantity CHECK (to_make >= 0),
+ lost numeric(10, 2) DEFAULT 0 CONSTRAINT positive_lost CHECK (lost >= 0),
+
+
+ order_id bigint REFERENCES production_order(id),
+ product_id bigint REFERENCES product(id)
+);
+
+CREATE TABLE production_service (
+ id serial NOT NULL PRIMARY KEY,
+ te_created_id bigint UNIQUE REFERENCES transaction_entry(id),
+ te_modified_id bigint UNIQUE REFERENCES transaction_entry(id),
+
+ quantity numeric(10, 2) CONSTRAINT positive_quantity CHECK (quantity >= 0),
+ service_id bigint REFERENCES service(id),
+ order_id bigint REFERENCES production_order(id)
+);
Modified: stoqlib/trunk/stoqlib/database/tables.py
==============================================================================
--- stoqlib/trunk/stoqlib/database/tables.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/database/tables.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -106,6 +106,10 @@
('commission', ["CommissionSource", "Commission"]),
('transfer', ["TransferOrder", "TransferOrderItem"]),
('inventory', ["Inventory", "InventoryItem"]),
+ ('production', ["ProductionOrder",
+ "ProductionItem",
+ "ProductionMaterial",
+ "ProductionService"]),
]
Modified: stoqlib/trunk/stoqlib/domain/exampledata.py
==============================================================================
--- stoqlib/trunk/stoqlib/domain/exampledata.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/domain/exampledata.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -98,6 +98,19 @@
def get_location(trans):
return ExampleCreator.create(trans, 'CityLocation')
+def create_production_order(trans):
+ return ExampleCreator.create(trans, 'ProductionOrder')
+
+def create_production_item(trans):
+ return ExampleCreator.create(trans, 'ProductionItem')
+
+def create_production_material(trans):
+ return ExampleCreator.create(trans, 'ProductionMaterial')
+
+def create_production_service(trans):
+ return ExampleCreator.create(trans, 'ProductionService')
+
+
class ExampleCreator(object):
def __init__(self, trans):
self.trans = trans
@@ -158,6 +171,10 @@
'Service': self.create_service,
'Till': self.create_till,
'UserProfile': self.create_user_profile,
+ 'ProductionOrder': self.create_production_order,
+ 'ProductionItem': self.create_production_item,
+ 'ProductionMaterial': self.create_production_material,
+ 'ProductionService': self.create_production_service,
}
if isinstance(model_type, basestring):
model_name = model_type
@@ -330,6 +347,41 @@
sellable=self.create_sellable(),
order=self.create_purchase_order())
+ def create_production_order(self):
+ from stoqlib.domain.production import ProductionOrder
+ return ProductionOrder(branch=self.create_branch(),
+ responsible=self.create_employee(),
+ description='production',
+ connection=self.trans)
+
+ def create_production_item(self):
+ from stoqlib.domain.product import ProductComponent
+ from stoqlib.domain.production import ProductionItem
+ product = self.create_product(10)
+ component = self.create_product(5)
+ ProductComponent(product=product,
+ component=component,
+ connection=self.trans)
+ return ProductionItem(product=product,
+ order=self.create_production_order(),
+ connection=self.trans)
+
+ def create_production_material(self):
+ from stoqlib.domain.production import ProductionMaterial
+ production_item = self.create_production_item()
+ component = list(production_item.get_components())[0]
+ return ProductionMaterial(product=component.component,
+ order=self.create_production_order(),
+ connection=self.trans)
+
+
+ def create_production_service(self):
+ from stoqlib.domain.production import ProductionService
+ service = self.create_service()
+ return ProductionService(service=service,
+ order=self.create_production_order(),
+ connection=self.trans)
+
def create_cfop_data(self):
from stoqlib.domain.fiscal import CfopData
return CfopData(connection=self.trans, code=u'123',
Modified: stoqlib/trunk/stoqlib/domain/product.py
==============================================================================
--- stoqlib/trunk/stoqlib/domain/product.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/domain/product.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -235,11 +235,17 @@
def get_components(self):
"""Returns the products which are our components.
- @returns: a sequence of Product instances
+ @returns: a sequence of ProductComponent instances
"""
- for component in ProductComponent.selectBy(
- product=self, connection=self.get_connection()):
- yield component
+ return ProductComponent.selectBy(product=self,
+ connection=self.get_connection())
+
+ def has_components(self):
+ """Returns if this product has components or not.
+
+ @returns: True if this product has components, False otherwise.
+ """
+ return self.get_components().count() > 0
def get_production_cost(self):
""" Return the production cost of a Product. The production cost
Modified: stoqlib/trunk/stoqlib/domain/production.py
==============================================================================
--- stoqlib/trunk/stoqlib/domain/production.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/domain/production.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -28,9 +28,10 @@
from zope.interface import implements
-from stoqlib.database.orm import UnicodeCol, ForeignKey, DateTimeCol, IntCol
+from stoqlib.database.orm import (UnicodeCol, ForeignKey, DateTimeCol, IntCol,
+ DecimalCol)
from stoqlib.domain.base import Domain
-from stoqlib.domain.interfaces import IContainer, IDescribable
+from stoqlib.domain.interfaces import IContainer, IDescribable, IStorable
from stoqlib.lib.translation import stoqlib_gettext
_ = stoqlib_gettext
@@ -66,25 +67,69 @@
status = IntCol(default=ORDER_OPENED)
open_date = DateTimeCol(default=datetime.datetime.now)
+ expected_start_date = DateTimeCol(default=None)
+ start_date = DateTimeCol(default=None)
close_date = DateTimeCol(default=None)
description = UnicodeCol(default='')
- responsible = ForeignKey('PersonAdaptToEmployee')
+ responsible = ForeignKey('PersonAdaptToEmployee', default=None)
branch = ForeignKey('PersonAdaptToBranch')
#
# IContainer implmentation
#
- #TODO: when implement ProductionItem.
-
def get_items(self):
- return []
+ return ProductionItem.selectBy(order=self,
+ connection=self.get_connection())
def add_item(self, sellable, quantity=Decimal(1)):
- return
+ return ProductionItem(order=self, product=sellable.product,
+ quantity=quantity,
+ connection=self.get_connection())
def remove_item(self, item):
- return
+ if item.order is not self:
+ raise ValueError('Argument item must have an order attribute '
+ 'associated with the current production '
+ 'order instance.')
+ ProductionItem.delete(item.id, connection=self.get_connection())
+
+ #
+ # Public API
+ #
+
+ def get_service_items(self):
+ """Returns all the services needed by this production.
+
+ @returns: a sequence of L{ProductionService} instances.
+ """
+ return ProductionService.selectBy(order=self,
+ connection=self.get_connection())
+
+ def get_material_items(self):
+ """Returns all the material needed by this production.
+
+ @returns: a sequence of L{ProductionMaterial} instances.
+ """
+ return ProductionMaterial.selectBy(order=self,
+ connection=self.get_connection())
+
+ def start_production(self):
+ """Start the production by allocating all the material needed.
+ """
+ assert self.status in [ProductionOrder.ORDER_OPENED,
+ ProductionOrder.ORDER_WAITING]
+
+ for material in self.get_material_items():
+ material.allocate()
+
+ self.start_date = datetime.date.today()
+ self.status = ProductionOrder.ORDER_PRODUCING
+
+ def set_production_waiting(self):
+ assert self.status == ProductionOrder.ORDER_OPENED
+
+ self.status = ProductionOrder.ORDER_WAITING
#
# IDescribable implementation
@@ -92,3 +137,122 @@
def get_description(self):
return self.description
+
+
+class ProductionItem(Domain):
+ """Production Item object implementation.
+
+ @ivar order: The L{ProductionOrder} of this item.
+ @ivar product: The product that will be manufactured.
+ @ivar quantity: The product's quantity that will be manufactured.
+ """
+ implements(IDescribable)
+
+ quantity = DecimalCol(default=1)
+ order = ForeignKey('ProductionOrder')
+ product = ForeignKey('Product')
+
+ #
+ # IDescribable Implementation
+ #
+
+ def get_description(self):
+ return self.product.sellable.get_description()
+
+ #
+ # Public API
+ #
+
+ def get_unit_description(self):
+ return self.product.sellable.get_unit_description()
+
+ def get_components(self):
+ return self.product.get_components()
+
+
+class ProductionMaterial(Domain):
+ """Production Material object implementation.
+
+ @ivar order: The L{ProductionOrder} that will consume this material.
+ @ivar product: The product that will be consumed.
+ @ivar needed: The quantity needed of this material.
+ @ivar consumed: The quantity already used of this material.
+ @ivar lost: The quantity lost of this material.
+ @ivar to_purchase: The quantity to purchase of this material.
+ @ivar to_make: The quantity to manufacture of this material.
+ """
+ implements(IDescribable)
+
+ needed = DecimalCol(default=1)
+ allocated = DecimalCol(default=0)
+ consumed = DecimalCol(default=0)
+ lost = DecimalCol(default=0)
+ to_purchase = DecimalCol(default=0)
+ to_make = DecimalCol(default=0)
+ order = ForeignKey('ProductionOrder')
+ product = ForeignKey('Product')
+
+ #
+ # Public API
+ #
+
+ def allocate(self):
+ """Allocates the needed quantity of this material by decreasing the
+ stock quantity. If the available quantity is not enough, then it will
+ allocate all the stock available.
+ """
+ stock = self.get_stock_quantity()
+ storable = IStorable(self.product, None)
+ assert storable is not None
+
+ if stock > self.needed:
+ quantity = self.needed
+ else:
+ quantity = stock
+
+ if quantity > 0:
+ self.allocated = quantity
+ storable.decrease_stock(quantity, self.order.branch)
+
+ #
+ # IDescribable Implementation
+ #
+
+ def get_description(self):
+ return self.product.sellable.get_description()
+
+ # Accessors
+
+ def get_unit_description(self):
+ return self.product.sellable.get_unit_description()
+
+ def get_stock_quantity(self):
+ storable = IStorable(self.product, None)
+ assert storable is not None
+ return storable.get_full_balance(self.order.branch)
+
+
+class ProductionService(Domain):
+ """Production Service object implementation.
+
+ @ivar order: The L{ProductionOrder} of this service.
+ @ivar service: The service that will be used by the production.
+ @ivar quantity: The service's quantity.
+ """
+ implements(IDescribable)
+
+ quantity = DecimalCol(default=1)
+ order = ForeignKey('ProductionOrder')
+ service = ForeignKey('Service')
+
+ #
+ # IDescribable Implementation
+ #
+
+ def get_description(self):
+ return self.service.sellable.get_description()
+
+ # Accessors
+
+ def get_unit_description(self):
+ return self.service.sellable.get_unit_description()
Modified: stoqlib/trunk/stoqlib/domain/service.py
==============================================================================
--- stoqlib/trunk/stoqlib/domain/service.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/domain/service.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -101,3 +101,7 @@
def get_unit(self):
return self.unit or u""
+
+ @property
+ def sellable(self):
+ return Sellable.get(self.id, connection=self.get_connection())
Modified: stoqlib/trunk/stoqlib/domain/test/test_product.py
==============================================================================
--- stoqlib/trunk/stoqlib/domain/test/test_product.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/domain/test/test_product.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -87,6 +87,15 @@
self.assertEqual(list(self.product.get_components()),
components)
+ def testHasComponents(self):
+ self.assertFalse(self.product.has_components())
+
+ component = self.create_product()
+ ProductComponent(product=self.product,
+ component=component,
+ connection=self.trans)
+ self.assertTrue(self.product.has_components())
+
def testGetProductionCost(self):
product = self.create_product()
sellable = product.sellable
Added: stoqlib/trunk/stoqlib/domain/test/test_production.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stoqlib/trunk/stoqlib/domain/test/test_production.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2009 Async Open Source <http://www.async.com.br>
+## All rights reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., or visit: http://www.gnu.org/.
+##
+## Author(s): George Y. Kussumoto <george at async.com.br>
+##
+""" This module test all class in stoqlib/domain/production.py """
+
+
+from stoqlib.domain.interfaces import IStorable
+from stoqlib.domain.production import ProductionOrder
+from stoqlib.domain.test.domaintest import DomainTest
+
+
+class TestProductionOrder(DomainTest):
+
+ def testGetServiceItems(self):
+ order = self.create_production_order()
+ self.assertEqual(list(order.get_service_items()), [])
+
+ service_item = self.create_production_service()
+ service_item.order = order
+ self.assertEqual(list(order.get_service_items()), [service_item])
+
+ def testGetMaterialItems(self):
+ order = self.create_production_order()
+ self.assertEqual(list(order.get_material_items()), [])
+
+ material_item = self.create_production_material()
+ material_item.order = order
+ self.assertEqual(list(order.get_material_items()), [material_item])
+
+ def testStartProduction(self):
+ order = self.create_production_order()
+ self.assertEqual(order.status, ProductionOrder.ORDER_OPENED)
+ order.start_production()
+ self.assertEqual(order.status, ProductionOrder.ORDER_PRODUCING)
+
+ def testSetProductionWaiting(self):
+ order = self.create_production_order()
+ self.assertEqual(order.status, ProductionOrder.ORDER_OPENED)
+ order.set_production_waiting()
+ self.assertEqual(order.status, ProductionOrder.ORDER_WAITING)
+
+
+class TestProductionMaterial(DomainTest):
+
+ def testAllocate(self):
+ material = self.create_production_material()
+ branch = material.order.branch
+ product = material.product
+ storable = IStorable(product, None)
+ if storable is None:
+ storable = product.addFacet(IStorable, connection=self.trans)
+ storable.increase_stock(10, branch)
+ material.needed = 10
+ stock_qty = material.get_stock_quantity()
+ self.assertEqual(stock_qty, 10)
+
+ material.allocate()
+ stock_qty = material.get_stock_quantity()
+ self.assertEqual(stock_qty, 0)
+ material.allocate()
+ self.assertEqual(stock_qty, 0)
Modified: stoqlib/trunk/stoqlib/domain/views.py
==============================================================================
--- stoqlib/trunk/stoqlib/domain/views.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/domain/views.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -136,6 +136,10 @@
clause = AND(ProductFullStockView.clause,
ProductComponent.q.productID == Product.q.id,)
+ @property
+ def sellable(self):
+ return Sellable.get(self.id, connection=self.get_connection())
+
class ProductWithStockView(ProductFullStockView):
"""Stores information about products, since product has a purchase or sale.
Added: stoqlib/trunk/stoqlib/gui/editors/productioneditor.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stoqlib/trunk/stoqlib/gui/editors/productioneditor.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2009 Async Open Source <http://www.async.com.br>
+## All rights reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., or visit: http://www.gnu.org/.
+##
+## Author(s): George Kussumoto <george at async.com.br>
+##
+""" Production editors """
+
+
+import sys
+
+import gtk
+
+from kiwi.datatypes import ValidationError
+
+from stoqlib.gui.editors.baseeditor import BaseEditor
+from stoqlib.domain.production import (ProductionItem, ProductionMaterial,
+ ProductionService)
+from stoqlib.lib.defaults import DECIMAL_PRECISION
+from stoqlib.lib.translation import stoqlib_gettext
+
+_ = stoqlib_gettext
+
+
+class ProductionItemEditor(BaseEditor):
+ gladefile = 'ProductionItemEditor'
+ model_type = ProductionItem
+ size = (-1, 150)
+ model_name = _(u'Production Item')
+ proxy_widgets = ['description', 'quantity', 'unit_description',]
+
+ def setup_location_widgets(self):
+ location = self.model.product.location
+ if location:
+ self.location.set_text(location)
+ else:
+ self.location.hide()
+ self.location_content.hide()
+
+ def setup_editor_widgets(self):
+ self.order_number.set_text("%04d" % self.model.order.id)
+ self.quantity.set_adjustment(gtk.Adjustment(lower=0, upper=sys.maxint,
+ step_incr=1))
+ self.quantity.set_digits(DECIMAL_PRECISION)
+
+ def setup_proxies(self):
+ self.setup_editor_widgets()
+ self.setup_location_widgets()
+ self.proxy = self.add_proxy(
+ self.model, ProductionItemEditor.proxy_widgets)
+
+ #
+ # Kiwi callbacks
+ #
+
+ def on_quantity__validate(self, widget, value):
+ if not value or value <= 0:
+ return ValidationError(_(u'This quantity should be positive.'))
+
+
+class ProductionServiceEditor(ProductionItemEditor):
+ model_type = ProductionService
+ model_name = _(u'Production Service')
+
+ def setup_proxies(self):
+ self.setup_editor_widgets()
+ self.location_content.hide()
+ self.proxy = self.add_proxy(
+ self.model, ProductionServiceEditor.proxy_widgets)
+
+
+class ProductionMaterialEditor(ProductionItemEditor):
+ model_type = ProductionMaterial
+ model_name = _(u'Production Material Item')
+ proxy_widgets = ['description',]
+
+ def setup_proxies(self):
+ self.setup_editor_widgets()
+ self.setup_location_widgets()
+ self.proxy = self.add_proxy(
+ self.model, ProductionMaterialEditor.proxy_widgets)
+
+ self._has_components = self.model.product.has_components()
+ if self._has_components:
+ proxy_field = 'to_make'
+ self.quantity_lbl.set_text(_(u'Quantity to make:'))
+ else:
+ proxy_field = 'to_purchase'
+ self.quantity_lbl.set_text(_(u'Quantity to purchase:'))
+
+ self.quantity.set_property('model-attribute', proxy_field)
+ self.proxy.add_widget(proxy_field, self.quantity)
+
+ #
+ # Kiwi Callbacks
+ #
+
+ def on_quantity__validate(self, widget, value):
+ if value and value < 0:
+ return ValidationError(_(u'This quantity should be positive.'))
Modified: stoqlib/trunk/stoqlib/gui/wizards/abstractwizard.py
==============================================================================
--- stoqlib/trunk/stoqlib/gui/wizards/abstractwizard.py Thu Jul 30 13:46:17 2009 (r8187)
+++ stoqlib/trunk/stoqlib/gui/wizards/abstractwizard.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -82,6 +82,7 @@
table = Sellable
item_table = None
summary_label_text = None
+ summary_label_column = 'total'
def __init__(self, wizard, previous, conn, model):
WizardEditorStep.__init__(self, conn, wizard, model, previous)
@@ -209,7 +210,8 @@
def _setup_summary(self):
# FIXME: Move this into AdditionListSlave
- self.summary = SummaryLabel(klist=self.slave.klist, column='total',
+ self.summary = SummaryLabel(klist=self.slave.klist,
+ column=self.summary_label_column,
label=self.summary_label_text,
value_format='<b>%s</b>')
self.summary.show()
Added: stoqlib/trunk/stoqlib/gui/wizards/productionwizard.py
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stoqlib/trunk/stoqlib/gui/wizards/productionwizard.py Fri Jul 31 11:01:36 2009 (r8188)
@@ -0,0 +1,523 @@
+# -*- coding: utf-8 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Copyright (C) 2009 Async Open Source <http://www.async.com.br>
+## All rights reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU Lesser General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU Lesser General Public License for more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., or visit: http://www.gnu.org/.
+##
+## Author(s): George Kussumoto <george at async.com.br>
+##
+""" Production wizard definition """
+
+import datetime
+from decimal import Decimal
+
+import pango
+
+from kiwi.datatypes import ValidationError, currency
+from kiwi.ui.widgets.list import Column, ColoredColumn
+
+from stoqlib.database.runtime import get_current_branch
+from stoqlib.domain.interfaces import IBranch, IStorable
+from stoqlib.domain.person import Person, PersonAdaptToEmployee
+from stoqlib.domain.production import (ProductionOrder, ProductionItem,
+ ProductionMaterial, ProductionService)
+from stoqlib.domain.service import ServiceView
+from stoqlib.domain.sellable import Sellable
+from stoqlib.domain.views import ProductComponentView
+from stoqlib.lib.translation import stoqlib_gettext
+from stoqlib.lib.parameters import sysparam
+from stoqlib.lib.validators import format_quantity
+from stoqlib.gui.base.dialogs import run_dialog
+from stoqlib.gui.base.wizards import WizardEditorStep, BaseWizard
+from stoqlib.gui.editors.productioneditor import (ProductionItemEditor,
+ ProductionMaterialEditor,
+ ProductionServiceEditor)
+from stoqlib.gui.wizards.abstractwizard import SellableItemStep
+
+_ = stoqlib_gettext
+
+
+#
+# Wizard Steps
+#
+
+
+class OpenProductionOrderStep(WizardEditorStep):
+ gladefile = 'OpenProductionOrderStep'
+ model_type = ProductionOrder
+ proxy_widgets = ['open_date',
+ 'expected_start_date',
+ 'order_number',
+ 'branch',
+ 'responsible',
+ 'description',]
+
+ def __init__(self, conn, wizard, model):
+ WizardEditorStep.__init__(self, conn, wizard, model)
+
+ def _fill_branch_combo(self):
+ # FIXME: Implement and use IDescribable on PersonAdaptToBranch
+ table = Person.getAdapterClass(IBranch)
+ branches = table.get_active_branches(self.conn)
+ items = [(s.person.name, s) for s in branches]
+ self.branch.prefill(sorted(items))
+
+ def _fill_responsible_combo(self):
+ employees = PersonAdaptToEmployee.selectBy(
+ status=PersonAdaptToEmployee.STATUS_NORMAL,
+ connection=self.conn)
+ self.responsible.prefill([(e.person.name, e) for e in employees])
+
+ def _setup_widgets(self):
+ self._fill_branch_combo()
+ self._fill_responsible_combo()
+ self.open_date.set_sensitive(False)
+
+ #
+ # WizardStep hooks
+ #
+
+ def post_init(self):
+ self.description.grab_focus()
+ self.table.set_focus_chain([self.branch, self.expected_start_date,
+ self.responsible, self.description])
+ self.register_validate_function(self.wizard.refresh_next)
+ self.force_validation()
+
+ def next_step(self):
+ return ProductionServiceStep(self.wizard, self, self.conn, self.model)
+
+ def has_previous_step(self):
+ return False
+
+ def setup_proxies(self):
+ self._setup_widgets()
+ self.proxy = self.add_proxy(
+ self.model, OpenProductionOrderStep.proxy_widgets)
+ self.proxy.update('order_number', u'%04d' % self.model.id)
+ # suggests a responsible for the production order
+ self.responsible.select_item_by_position(0)
+
+ #
+ # Kiwi Callbacks
+ #
+
+ def on_expected_start_date__validate(self, widget, value):
+ today = datetime.date.today()
+ if value and value < today:
+ return ValidationError(
+ _(u'Expected start date should be a future date.'))
+
+
+class ProductionServiceStep(SellableItemStep):
+ model_type = ProductionOrder
+ item_table = ProductionService
+ summary_label_text = "<b>%s</b>" % _('Total:')
+ summary_label_column = 'quantity'
+
+ #
+ # Helper methods
+ #
+
+ def setup_sellable_entry(self):
+ max_results = sysparam(self.conn).MAX_SEARCH_RESULTS
+ service_view_items = ServiceView.select(
+ ServiceView.q.status == Sellable.STATUS_AVAILABLE,
+ connection=self.conn,
+ limit=max_results)
+ delivery = sysparam(self.conn).DELIVERY_SERVICE
+ self.sellable.prefill([(s.description, s.sellable)
+ for s in service_view_items if s.id != delivery.id])
+
+ def setup_slaves(self):
+ SellableItemStep.setup_slaves(self)
+ self.item_lbl.set_text(_(u'Services:'))
+ self.hide_add_button()
+ self.product_button.hide()
+ self.cost_label.hide()
+ self.cost.hide()
+
+ self.quantity.connect('validate', self._on_quantity__validate)
+
+ def _get_production_service_by_sellable(self, sellable):
+ service = sellable.service
+ for item in self.slave.klist:
+ if item.service is service:
+ return item
+
+ #
+ # SellableItemStep virtual methods
+ #
+
+ def validate(self, value):
+ # This step is optional
+ return True
+
+ def get_order_item(self, sellable, cost, quantity):
+ item = self._get_production_service_by_sellable(sellable)
+ if item is None:
+ return ProductionService(service=sellable.service,
+ quantity=quantity,
+ order=self.model,
+ connection=self.conn)
+ item.quantity += quantity
+ return item
+
+ def get_saved_items(self):
+ return list(self.model.get_service_items())
+
+ def get_columns(self):
+ return [
+ Column('service.sellable.code', title=_('Code'), data_type=str),
+ Column('service.sellable.category_description', title=_('Category'),
+ data_type=str, expand=True),
+ Column('service.sellable.description', title=_('Description'),
+ data_type=str, expand=True, sorted=True),
+ Column('quantity', title=_('Quantity'), data_type=Decimal,
+ format_func=format_quantity),
+ Column('service.sellable.unit_description',title=_('Unit'),
+ data_type=str),
+ Column('service.sellable.cost', title=_('Cost'),
+ data_type=currency),]
+
+ #
+ # WizardStep hooks
+ #
+
+ def post_init(self):
+ SellableItemStep.post_init(self)
+ self.slave.set_editor(ProductionServiceEditor)
+
+ def next_step(self):
+ return ProductionItemStep(self.wizard, self, self.conn, self.model)
+
+ #
+ # Callbacks
+ #
+
+ def _on_quantity__validate(self, widget, value):
+ if not value or value <= Decimal(0):
+ return ValidationError(_(u'Quantity must be greater than zero'))
+
+
+class ProductionItemStep(SellableItemStep):
+ """ Wizard step for production items selection """
+ model_type = ProductionOrder
+ item_table = ProductionItem
+ summary_label_text = "<b>%s</b>" % _('Total:')
+ summary_label_column = 'quantity'
+
+ #
+ # Helper methods
+ #
+
+ def setup_sellable_entry(self):
+ max_results = sysparam(self.conn).MAX_SEARCH_RESULTS
+ composable_items = ProductComponentView.select(connection=self.conn,
+ limit=max_results)
+ self.sellable.prefill(
+ [(c.description, c.sellable) for c in composable_items])
+
+ def setup_slaves(self):
+ SellableItemStep.setup_slaves(self)
+ self.hide_add_button()
+ self.product_button.hide()
+ self.cost_label.hide()
+ self.cost.hide()
+
+ self.quantity.connect('validate', self._on_quantity__validate)
+
+ def _get_production_item_by_sellable(self, sellable):
+ product = sellable.product
+ for item in self.slave.klist:
+ if item.product is product:
+ return item
+
+ #
+ # SellableItemStep virtual methods
+ #
+
+ def get_order_item(self, sellable, cost, quantity):
+ item = self._get_production_item_by_sellable(sellable)
+ if item is None:
+ return self.model.add_item(sellable, quantity)
+
+ item.quantity += quantity
+ return item
+
+ def get_saved_items(self):
+ return list(self.model.get_items())
+
+ def get_columns(self):
+ return [
+ Column('product.sellable.code', title=_('Code'), data_type=str),
+ Column('product.sellable.category_description', title=_('Category'),
+ data_type=str, expand=True),
+ Column('product.sellable.description', title=_('Description'),
+ data_type=str, expand=True, sorted=True),
+ Column('quantity', title=_('Quantity'), data_type=Decimal,
+ format_func=format_quantity),
+ Column('product.sellable.unit_description',title=_('Unit'),
+ data_type=str),]
+
+ #
+ # WizardStep hooks
+ #
+
+ def post_init(self):
+ SellableItemStep.post_init(self)
+ self.slave.set_editor(ProductionItemEditor)
+
+ def next_step(self):
+ return FinishOpenProductionOrderStep(self.wizard, self, self.conn,
+ self.model)
+
+ #
+ # Callbacks
+ #
+
+ def _on_quantity__validate(self, widget, value):
+ if not value or value <= Decimal(0):
+ return ValidationError(_(u'Quantity must be greater than zero'))
+
+
+#XXX: This is just a workaround to avoid the zillions of queries
+# when handling production items and materials.
+class _TemporaryMaterial(object):
+ def __init__(self, production, component, conn):
+ storable = IStorable(component, None)
+ if storable is not None:
+ self.stock_quantity = storable.get_full_balance(production.branch)
+ else:
+ self.stock_quantity = 0
+
+ sellable = component.sellable
+ self.code = sellable.code
+ self.description = sellable.get_description()
+ self.category_description = sellable.get_description()
+ self.unit_description = sellable.get_unit_description()
+ self.product = component
+ self.needed = 0
+ self.to_purchase = 0
+ self.to_make = 0
+ self.order = production
+ self._material = None
+ self._conn = conn
+
+ @property
+ def material(self):
+ if self._material is None:
+ # At this point, the needed quantity have already been updated.
+ assert self.needed > 0
+ material = ProductionMaterial.selectOneBy(order=self.order,
+ product=self.product,
+ connection=self._conn)
+ if material is not None:
+ self._material = material
+ self._material.needed = self.needed
+ else:
+ self._material = ProductionMaterial(
+ needed=self.needed,
+ to_purchase=self.to_purchase,
+ to_make=self.to_make,
+ order=self.order,
+ product=self.product,
+ connection=self._conn)
+ return self._material
+
+ def create(self):
+ return self.material
+
+ def sync(self):
+ #assert self._material is not None
+ self.to_purchase = self.material.to_purchase
+ self.to_make = self.material.to_make
+
+ def add_quantity(self, quantity):
+ assert quantity > 0
+ self.needed += quantity
+ missing_quantity = self.needed - self.stock_quantity
+ if missing_quantity > 0:
+ if self.product.has_components():
+ self.to_make = missing_quantity
+ else:
+ self.to_purchase = missing_quantity
+ if self._material is not None:
+ self._material.needed = self.needed
+ self._material.to_make = self.to_make
+ self._material.to_purchase = self.to_purchase
+
+
+class FinishOpenProductionOrderStep(WizardEditorStep):
+ gladefile = 'FinishOpenProductionOrderStep'
+ model_type = ProductionOrder
+ proxy_widgets = []
+
+ def __init__(self, wizard, previous, conn, model):
+ WizardEditorStep.__init__(self, conn, wizard, model, previous)
+ self._setup_widgets()
+
+ def _add_materials(self, production_item):
+ self._materials_objects = []
+ for product_component in production_item.get_components():
+ material = self._get_or_create_material(product_component)
+ quantity = product_component.quantity * production_item.quantity
+ material.add_quantity(quantity)
+ material.sync()
+
+ if material not in self.materials:
+ self.materials.append(material)
+ else:
+ self.materials.update(material)
+
+ def _get_or_create_material(self, product_component):
+ component = product_component.component
+ for material in self.materials:
+ if material.product is component:
+ return material
+ return _TemporaryMaterial(self.model, component, self.conn)
+
+ def _edit_production_material(self):
+ material = self.materials.get_selected()
+ assert material is not None
+
+ retval = run_dialog(ProductionMaterialEditor, self, self.conn,
+ material.material)
+ if retval:
+ material.sync()
+ self.materials.update(material)
+
+ def _setup_widgets(self):
+ self.edit_button.set_sensitive(False)
+ self.materials.set_columns(self._get_columns())
+ for production_item in self.model.get_items():
+ self._add_materials(production_item)
+
+ def _get_columns(self):
+ return [
+ Column('code', title=_('Code'), data_type=str),
+ Column('category_description', title=_('Category'),
+ data_type=str, expand=True, ellipsize=pango.ELLIPSIZE_END),
+ Column('description', title=_('Description'), data_type=str,
+ expand=True, ellipsize=pango.ELLIPSIZE_END, sorted=True),
+ Column('unit_description',title=_('Unit'),
+ data_type=str),
+ Column('needed', title=_('Needed'), data_type=Decimal,
+ format_func=format_quantity),
+ Column('stock_quantity', title=_('In Stock'), data_type=Decimal,
+ format_func=format_quantity),
+ ColoredColumn('to_purchase', title=_('To Purchase'),
+ data_type=Decimal, format_func=format_quantity,
+ use_data_model=True, color='red',
+ data_func=self._colorize_to_purchase_col),
+ ColoredColumn('to_make', title=_('To Make'), data_type=Decimal,
+ format_func=format_quantity, use_data_model=True,
+ color='red', data_func=self._colorize_to_make_col),]
+
+ #XXX: Some duplication here, since the columns will never be both red.
+
+ def _colorize_to_purchase_col(self, material):
+ if material.product.has_components():
+ return
+ stock_qty = material.stock_quantity
+ if material.to_purchase + stock_qty - material.needed < 0:
+ return True
+ return False
+
+ def _colorize_to_make_col(self, material):
+ if not material.product.has_components():
+ return
+ stock_qty = material.stock_quantity
+ if material.to_make + stock_qty - material.needed < 0:
+ return True
+ return False
+
+ #
+ # WizardStep hooks
+ #
+
+ def has_next_step(self):
+ return False
+
+ def post_init(self):
+ self.register_validate_function(self.wizard.refresh_next)
+ self.force_validation()
+
+ def setup_proxies(self):
+ self.proxy = self.add_proxy(self.model, self.proxy_widgets)
+
+ def validate_step(self):
+ # Used as a hook for the finish button
+ for material in self.materials:
+ material.create()
+
+ if self.start_production_check.get_active():
+ self.model.start_production()
+ elif self.model.status != ProductionOrder.ORDER_WAITING:
+ for material in self.materials:
+ if material.to_purchase > 0 or material.to_make > 0:
+ self.model.set_production_waiting()
+ break
+ return True
+
+ #
+ # Kiwi Callbacks
+ #
+
+ def on_materials__selection_changed(self, widget, material):
+ self.edit_button.set_sensitive(bool(material))
+
+ def on_materials__double_click(self, widget, material):
+ self._edit_production_material()
+
+ def on_edit_button__clicked(self, widget):
+ self._edit_production_material()
+
+
+#
+# Main wizard
+#
+
+
+class ProductionWizard(BaseWizard):
+ size = (775, 400)
+
+ def __init__(self, conn, model=None, edit_mode=False):
+ title = self._get_title(model)
+ model = model or self._create_model(conn)
+ first_step = OpenProductionOrderStep(conn, self, model)
+ BaseWizard.__init__(self, conn, first_step, model, title=title,
+ edit_mode=edit_mode)
+
+ def _get_title(self, model=None):
+ if not model:
+ return _(u'New Production')
+ return _(u'Edit Production')
+
+ def _create_model(self, conn):
+ branch = get_current_branch(conn)
+ return ProductionOrder(branch=branch,
+ connection=conn)
+
+ #
+ # WizardStep hooks
+ #
+
+ def finish(self):
+ self.retval = self.model
+ self.close()
More information about the POS-commit
mailing list