class PaymentChangeHistory(Domain): """ A class to hold information about changes to a payment. Only one tuple (last_due_date, new_due_date) or (last_status, new_status) should be non-null at a time. See also: `schema <http://doc.stoq.com.br/schema/tables/payment_change_history.html>`__ """ __storm_table__ = 'payment_change_history' payment_id = IdCol() #: the changed |payment| payment = Reference(payment_id, 'Payment.id') #: the reason of the change change_reason = UnicodeCol(default=None) #: when the changed happened change_date = DateTimeCol(default_factory=localnow) #: the due date that was set before the changed last_due_date = DateTimeCol(default=None) #: the due date that was set after changed new_due_date = DateTimeCol(default=None) #: status before the change last_status = EnumCol(allow_none=False, default=Payment.STATUS_PREVIEW) #: status after change new_status = EnumCol(allow_none=False, default=Payment.STATUS_PREVIEW)
class ProfileSettings(Domain): """Profile settings for user profile instances. Each instance of this class stores information about the access availability in a certain application.""" __storm_table__ = 'profile_settings' #: The user profile that has these settings. user_profile_id = IdCol() user_profile = Reference(user_profile_id, 'UserProfile.id') #: The app name this user has or does not have access to. app_dir_name = UnicodeCol() #: Has this user permission to use this app? has_permission = BoolCol(default=False) @classmethod def set_permission(cls, store, profile, app, permission): """ Set the permission for a user profile to use a application :param store: a store :param profile: a UserProfile :param app: name of the application :param permission: a boolean of the permission """ setting = store.find(cls, user_profile=profile, app_dir_name=app).one() setting.has_permission = permission
class QuoteGroup(Domain): __storm_table__ = 'quote_group' #: A numeric identifier for this object. This value should be used instead of #: :obj:`Domain.id` when displaying a numerical representation of this object to #: the user, in dialogs, lists, reports and such. identifier = IdentifierCol() branch_id = IdCol() branch = Reference(branch_id, 'Branch.id') # # IContainer # def get_items(self): return self.store.find(Quotation, group=self) def remove_item(self, item): if item.group is not self: raise ValueError( _(u'You can not remove an item which does not ' u'belong to this group.')) order = item.purchase # FIXME: Bug 5581 Removing objects with synced databases is dangerous. # Investigate this usage self.store.remove(item) for order_item in order.get_items(): order.remove_item(order_item) self.store.remove(order) def add_item(self, item): store = self.store return Quotation(purchase=item, group=self, branch=self.branch, store=store) # # IDescribable # def get_description(self): return _(u"quote number %s") % self.identifier # # Public API # def cancel(self): """Cancel a quote group.""" store = self.store for quote in self.get_items(): quote.close() # FIXME: Bug 5581 Removing objects with synced databases is # dangerous. Investigate this usage store.remove(quote)
class ProductCofinsTemplate(BaseCOFINS): """Template of COFINS tax""" __storm_table__ = 'product_cofins_template' product_tax_template_id = IdCol() product_tax_template = Reference(product_tax_template_id, 'ProductTaxTemplate.id')
class ProductIpiTemplate(BaseIPI): """Template of IPI tax""" __storm_table__ = 'product_ipi_template' product_tax_template_id = IdCol() product_tax_template = Reference(product_tax_template_id, 'ProductTaxTemplate.id')
class ProductPisTemplate(BasePIS): """Template of PIS tax""" __storm_table__ = 'product_pis_template' product_tax_template_id = IdCol() product_tax_template = Reference(product_tax_template_id, 'ProductTaxTemplate.id')
class CreditProvider(Domain): """A credit provider This is the institution that provides the credit to the client, for instance: American Express, Visanet, Redecard, etc... """ __storm_table__ = 'credit_provider' #: A short description of this provider short_name = UnicodeCol() #: An identification for this provider provider_id = UnicodeCol(default=u'') #: the maximum number of installments for a |sale| using this credit provider. max_installments = IntCol(default=1) default_device_id = IdCol() #: The default device for this credit provider. This will be suggested to #: the user when he selects this provider in the checkout dialog default_device = Reference(default_device_id, 'CardPaymentDevice.id') #: The date when we start working with this provider open_contract_date = DateTimeCol() # # IDescribable # def get_description(self): return self.short_name # # Public API # @classmethod def get_provider_by_provider_id(cls, provider_id, store): """Get a provider given a provider id string :param provider_id: a string representing the provider :param store: a database store """ return store.find(cls, provider_id=provider_id) @classmethod def get_card_providers(cls, store): """Get a list of all credit card providers. :param store: a database store """ return store.find(cls) @classmethod def has_card_provider(cls, store): """Find out if there is a card provider :param store: a database store :returns: if there is a card provider """ return bool(store.find(cls).count())
class TillEntry(Domain): """A TillEntry is a representing cash added or removed in a |till|. * A positive value represents addition. * A negative value represents removal. """ __storm_table__ = 'till_entry' #: A numeric identifier for this object. This value should be used instead of #: :obj:`Domain.id` when displaying a numerical representation of this object to #: the user, in dialogs, lists, reports and such. identifier = IdentifierCol() #: the date the entry was created date = DateTimeCol(default_factory=localnow) #: A small string describing what was done description = UnicodeCol() #: value of transaction value = PriceCol() till_id = IdCol(allow_none=False) #: the |till| the entry takes part of till = Reference(till_id, 'Till.id') payment_id = IdCol(default=None) #: |payment| of this entry, if any payment = Reference(payment_id, 'Payment.id') branch_id = IdCol() #: |branch| that received or gave money branch = Reference(branch_id, 'Branch.id') @property def time(self): """The time of the entry Note that this is the same as :obj:`.date.time()`, but with microseconds replaced to *0*. """ time = self.date.time() return time.replace(microsecond=0)
class Message(Domain): """A message that will be displayed at the launcher screen. """ __storm_table__ = 'message' #: The content of the message content = UnicodeCol(default=u'') #: When this message was created created_at = DateTimeCol(default_factory=localnow) #: Until when this message will be shown expire_at = DateTimeCol() created_by_id = IdCol(default=None) #: The user that created this message created_by = Reference(created_by_id, 'LoginUser.id') branch_id = IdCol() #: the branch this message will be displayed at branch = Reference(branch_id, 'Branch.id') profile_id = IdCol() #: the user this message will be displayed to profile = Reference(profile_id, 'UserProfile.id') user_id = IdCol() #: the user profile this message will be displayed to user = Reference(user_id, 'LoginUser.id') @classmethod def find_active(cls, store): branch = api.get_current_branch(store) user = api.get_current_user(store) profile = user.profile now = localnow() query = And( # All fields are optional, so default to the current user (or now) if they are missing Coalesce(cls.expire_at, now) >= now, Coalesce(cls.branch_id, branch.id) == branch.id, Coalesce(cls.user_id, user.id) == user.id, Coalesce(cls.profile_id, profile.id) == profile.id, ) return store.find(cls, query)
class NFeSupplier(Domain): __storm_table__ = 'nfe_supplier' #: The CNPJ of the supplier cnpj = UnicodeCol(default=u"") #: The real name of the supplier name = UnicodeCol(default=u"") #: The fancy name of the supplier fancy_name = UnicodeCol(default=u"") #: Postal code postal_code = UnicodeCol(default=u"") #: Address commplement (ex: ap 22) complement = UnicodeCol(default=u"") #: The supplier district district = UnicodeCol(default=u"") #: The street/avenue name street = UnicodeCol(default=u"") #: Primary phone phone_number = UnicodeCol(default=u"") #: Street number street_number = IntCol() #: Municipal registry if have both product and service in the same invoice municipal_registry = UnicodeCol(default=u"") #: IBGE's State Code is national 2 digit registry (ex: 29 = Bahia) state_registry = UnicodeCol(default=u"") #: IBGE's City Code is national 7 digit registry (ex: 2927408 = Salvador) city_code = IntCol() #: Stoq supplier id supplier_id = IdCol() #: Stoq supplier reference supplier = Reference(supplier_id, "Supplier.id") @property def state(self): city_location = self.store.find(CityLocation, city_code=self.city_code).one() return city_location.state @property def country(self): city_location = self.store.find(CityLocation, city_code=self.city_code).one() return city_location.country
class CardPaymentDevice(Domain): """An eletronic device used to charge the client. Each device may have different costs for the company, depending on the contract between them. These costs should be configured using |cardcost| """ __storm_table__ = 'card_payment_device' #: How much the it costs the shop per month to have this device monthly_cost = PriceCol() #: user-defined description of the device, like "Mastercard reader" description = UnicodeCol() #: The |supplier| id for this device supplier_id = IdCol() supplier = Reference(supplier_id, 'Supplier.id') def get_description(self): return self.description def get_provider_cost(self, provider, card_type, installments): query = And(CardOperationCost.device_id == self.id, CardOperationCost.provider_id == provider.id, CardOperationCost.card_type == card_type, CardOperationCost.installment_start <= installments, CardOperationCost.installment_end >= installments) return self.store.find(CardOperationCost, query).one() def get_all_costs(self): return self.store.find(CardOperationCost, device=self) @classmethod def get_devices(cls, store): return store.find(cls) @classmethod def delete(cls, id, store): """Removes a device from the database. Since devices may be referenced by |cardcost| and |creditcarddata| objects, this method will also: * Remove all |cardcost| objects * Update all references to this device by |creditcarddata| objects to ``None``. """ CardOperationCost.delete_from_device(id, store) vals = {CreditCardData.device_id: None} clause = CreditCardData.device_id == id store.execute(Update(vals, clause, CreditCardData)) store.remove(store.get(cls, id))
class Invoice(Domain): __storm_table__ = 'invoice' TYPE_IN = u'in' TYPE_OUT = u'out' invoice_number = IntCol() operation_nature = UnicodeCol() invoice_type = EnumCol(allow_none=False) branch_id = IdCol()
class ProductPisTemplate(BasePIS): """Template of PIS tax""" __storm_table__ = 'product_pis_template' product_tax_template_id = IdCol() product_tax_template = Reference(product_tax_template_id, 'ProductTaxTemplate.id') def get_description(self): return self.product_tax_template.name
class ProductCofinsTemplate(BaseCOFINS): """Template of COFINS tax""" __storm_table__ = 'product_cofins_template' product_tax_template_id = IdCol() product_tax_template = Reference(product_tax_template_id, 'ProductTaxTemplate.id') def get_description(self): return self.product_tax_template.name
class ProductStockItem(Domain): """Class that makes a reference to the |product| stock of a certain |branch|. See also: `schema <http://doc.stoq.com.br/schema/tables/product_stock_item.html>`__ """ __storm_table__ = 'product_stock_item' #: the average stock price, will be updated as new stock items are #: received. stock_cost = PriceCol(default=0) #: number of storables in the stock item quantity = QuantityCol(default=0) branch_id = IdCol() #: the |branch| this stock item belongs to branch = Reference(branch_id, 'Branch.id') storable_id = IdCol() #: the |storable| the stock item refers to storable = Reference(storable_id, 'Storable.id') batch_id = IdCol() #: The |batch| that the storable is in. batch = Reference(batch_id, 'StorableBatch.id') def update_cost(self, new_quantity, new_cost): """Update the stock_item according to new quantity and cost. :param new_quantity: The new quantity added to stock. :param new_cost: The cost of one unit of the added stock. """ total_cost = self.quantity * self.stock_cost total_cost += new_quantity * new_cost total_items = self.quantity + new_quantity self.stock_cost = total_cost / total_items
class ClientCategoryPrice(Domain): """A table that stores special prices for |clients| based on their |clientcategory|. See also: `schema <http://doc.stoq.com.br/schema/tables/client_category_price.html>`__ """ __storm_table__ = 'client_category_price' sellable_id = IdCol() #: The |sellable| that has a special price sellable = Reference(sellable_id, 'Sellable.id') category_id = IdCol() #: The |clientcategory| that has the special price category = Reference(category_id, 'ClientCategory.id') #: The price for this (|sellable|, |clientcategory|) price = PriceCol(default=0) #: The max discount that may be applied. max_discount = PercentCol(default=0) @property def markup(self): if self.sellable.cost == 0: return Decimal(0) return ((self.price / self.sellable.cost) - 1) * 100 @markup.setter def markup(self, markup): self.price = self.sellable._get_price_by_markup(markup) @property def category_name(self): return self.category.name def remove(self): """Removes this client category price from the database.""" self.store.remove(self)
class Book(Domain): """ A book class for products, holding specific data about books """ __storm_table__ = 'book' product_id = IdCol() product = Reference(product_id, 'Product.id') publisher_id = IdCol(default=None) publisher = Reference(publisher_id, 'BookPublisher.id') author = UnicodeCol(default=u'') series = UnicodeCol(default=u'') edition = UnicodeCol(default=u'') subject = UnicodeCol(default=u'') isbn = UnicodeCol(default=u'') language = UnicodeCol(default=u'') decorative_finish = UnicodeCol(default=u'') country = UnicodeCol(default=u'Brazil') pages = IntCol(default=0) year = IntCol(default=0) synopsis = UnicodeCol(default=u'')
class InvoicePrinter(Domain): """An invoice printer is a representation of a physical printer connected to a branch station. It has a layout assigned which will be used to format the data sent to the printer """ __storm_table__ = 'invoice_printer' #: a operating system specific identifier for the #: device used to send the printer job, /dev/lpX on unix device_name = UnicodeCol() #: a human friendly description of the printer, this #: will appear in interfaces description = UnicodeCol() #: the station this printer is connected to station_id = IdCol() station = Reference(station_id, 'BranchStation.id') #: the layout used to format the invoices layout_id = IdCol() layout = Reference(layout_id, 'InvoiceLayout.id') def get_description(self): """ Gets the description of the printer. :returns: description """ return self.description @classmethod def get_by_station(cls, station, store): """Gets the printer given a station. If there's no invoice printer configured for this station, return None. :param station: the station :param store: a store :returns: an InvoiceLayout or None """ return store.find(InvoicePrinter, station=station).one()
class TillSummary(Domain): """A TillSummary is a summary of the state of all payment methods when the till was closed. """ __storm_table__ = 'till_summary' #: The value that was registerd in Stoq when the till was closed. system_value = PriceCol() #: The value the user informed when closing the till. This can be different than the #: system value when using a blind till closing process (where the user does not #: know the system value) user_value = PriceCol() #: When using a blind closing process, this is the value that the manager verified #: that actually was on the till. All values can differ. verify_value = PriceCol() notes = UnicodeCol() method_id = IdCol(allow_none=False) #: The method this item is for method = Reference(method_id, 'PaymentMethod.id') provider_id = IdCol(allow_none=True) provider = Reference(provider_id, 'CreditProvider.id') card_type = EnumCol(allow_none=True) till_id = IdCol(allow_none=False) #: the |till| this item takes part of till = Reference(till_id, 'Till.id') @property def description(self): if not self.card_type: return self.method.get_description() return '%s %s %s' % (self.method.get_description(), self.provider.short_name, CreditCardData.short_desc[self.card_type])
class UIField(Domain): """This describes a field in form a. Can be used makae fields mandatory or hide them completely. """ __storm_table__ = 'ui_field' ui_form_id = IdCol() ui_form = Reference(ui_form_id, 'UIForm.id') field_name = UnicodeCol() description = UnicodeCol() visible = BoolCol() mandatory = BoolCol()
class NFePayment(Domain): __storm_table__ = "nfe_payment" nfe_purchase_id = IdCol() nfe_purchase = Reference(nfe_purchase_id, "NFePurchase.id") method_id = IdCol() #: Payment method (ex: bill, credit card, etc) method = Reference(method_id, "PaymentMethod.id") #: The final value of the payment value = PriceCol() #: Installment number duplicate_number = UnicodeCol() #: The final date for the payment due_date = DateTimeCol(default=None) #: The description generated by the system for the payment description = UnicodeCol(default=u"")
class ProfileSettings(Domain): """Profile settings for user profile instances. Each instance of this class stores information about the access availability in a certain application.""" __storm_table__ = 'profile_settings' #: The user profile that has these settings. user_profile_id = IdCol() user_profile = Reference(user_profile_id, 'UserProfile.id') #: The app name this user has or does not have access to. app_dir_name = UnicodeCol() #: Has this user permission to use this app? has_permission = BoolCol(default=False) #: Virtual apps. They will have permission if one of the apps mapped #: on the list have permission virtual_apps = { 'link': ['admin'], } @classmethod def get_permission(cls, store, profile, app): """Check if a profile has access to an app :param store: A store :param profile: The :class:`.UserProfile` to check for permission :param app: The name of the application :return: Whether the profile has access to the profile or not """ apps = [app] + cls.virtual_apps.get(app, []) res = store.find( cls, And(cls.user_profile_id == profile.id, Eq(cls.has_permission, True), cls.app_dir_name.is_in(apps))) res.config(limit=1) return res.one() is not None @classmethod def set_permission(cls, store, profile, app, permission): """ Set the permission for a user profile to use a application :param store: a store :param profile: a UserProfile :param app: name of the application :param permission: a boolean of the permission """ setting = store.find(cls, user_profile=profile, app_dir_name=app).one() setting.has_permission = permission
class PurchaseReceivingMap(Domain): """This class stores a map for purchase and receivings. One purchase may be received more than once, for instance, if it was shippped in more than one package. Also, a receiving may be for different purchase orders, if more than one purchase order was shipped in the same package. """ __storm_table__ = 'purchase_receiving_map' purchase_id = IdCol() #: The purchase that was recieved purchase = Reference(purchase_id, 'PurchaseOrder.id') receiving_id = IdCol() #: In which receiving the purchase was received. receiving = Reference(receiving_id, 'ReceivingOrder.id')
class ProductBranchOverride(Domain): __storm_table__ = 'product_branch_override' location = UnicodeCol() icms_template_id = IdCol() icms_template = Reference(icms_template_id, 'ProductIcmsTemplate.id') ipi_template_id = IdCol() ipi_template = Reference(ipi_template_id, 'ProductIpiTemplate.id') pis_template_id = IdCol() pis_template = Reference(pis_template_id, 'ProductPisTemplate.id') cofins_template_id = IdCol() cofins_template = Reference(cofins_template_id, 'ProductCofinsTemplate.id') branch_id = IdCol() branch = Reference(branch_id, 'Branch.id') product_id = IdCol() product = Reference(product_id, 'Product.id') @classmethod def find_product(cls, branch: Branch, product): return product.store.find(cls, product=product, branch=branch).one()
class Quotation(IdentifiableDomain): __storm_table__ = 'quotation' #: A numeric identifier for this object. This value should be used instead of #: :obj:`Domain.id` when displaying a numerical representation of this object to #: the user, in dialogs, lists, reports and such. identifier = IdentifierCol() group_id = IdCol() group = Reference(group_id, 'QuoteGroup.id') purchase_id = IdCol() purchase = Reference(purchase_id, 'PurchaseOrder.id') branch_id = IdCol() branch = Reference(branch_id, 'Branch.id') station_id = IdCol(allow_none=False) #: The station this object was created at station = Reference(station_id, 'BranchStation.id') def get_description(self): supplier = self.purchase.supplier.person.name return u"Group %s - %s" % (self.group.identifier, supplier) # # Public API # def close(self): """Closes the quotation""" # we don't have a specific status for closed quotes, so we just # cancel it if not self.is_closed(): self.purchase.cancel() def is_closed(self): """Returns if the quotation is closed or not. :returns: True if the quotation is closed, False otherwise. """ return self.purchase.status == PurchaseOrder.ORDER_CANCELLED
class FiscalSaleHistory(Domain): """Holds fiscal information about the sales. """ (TYPE_CPF, TYPE_CNPJ) = range(2) __storm_table__ = 'fiscal_sale_history' document_type = IntCol(default=TYPE_CPF) document = UnicodeCol(default=None) sale_id = IdCol() sale = Reference(sale_id, 'Sale.id') coo = IntCol(default=0) document_counter = IntCol(default=0)
class OpticalMedic(Domain): """Information about the Medic (Ophtamologist)""" __storm_table__ = 'optical_medic' person_id = IdCol(allow_none=False) person = Reference(person_id, 'Person.id') # TODO: Find out a better name for crm crm_number = UnicodeCol() #: If this medic is a partner of the store, ie, if they recomend clients to #: this store partner = BoolCol() # # IDescribable implementation # @classmethod def get_person_by_crm(cls, store, document): query = cls.crm_number == document tables = [ Person, Join(OpticalMedic, Person.id == OpticalMedic.person_id) ] return store.using(*tables).find(Person, query).one() def get_description(self): return _('%s (upid: %s)') % (self.person.name, self.crm_number) @DomainMergeEvent.connect @classmethod def on_domain_merge(cls, obj, other): if type(obj) != Person: return this_facet = obj.store.find(cls, person=obj).one() other_facet = obj.store.find(cls, person=other).one() if not this_facet and not other_facet: return # If this facet does not have a crm, but the other one does, the crm would be copied to # this, but we need to clear the other value, since crm is unique in the database. if other_facet and other_facet.crm_number and not this_facet.crm_number: crm = other_facet.crm_number other_facet.crm_number = None this_facet.crm_number = crm obj.merge_facet(this_facet, other_facet) return set([('optical_medic', 'person_id')])
class StorableBatch(Domain): """Batch information for storables. A batch is a colection of products (storable) that were produced at the same time and thus they have some common information, such as expiration date. This information is useful since sometimes its necessary to make decisions based on the batch like a special promotion for older batches (if it is close to the expiration date, for instance) or if a batch is somehow defective and we need to contact the clients that purchased items from this batch. """ __storm_table__ = 'storable_batch' #: The sequence number for this batch. Should be unique for a given #: storable batch_number = UnicodeCol(allow_none=False) #: The date this batch was created create_date = DateTimeCol(default_factory=localnow) #: An expiration date, specially for perishable products, like milk and food in #: general expire_date = DateTimeCol() #: Some space for the users to add notes to this batch. notes = UnicodeCol() storable_id = IdCol(allow_none=False) #: The storable that is in this batch storable = Reference(storable_id, 'Storable.id') def get_balance_for_branch(self, branch): """Return the stock balance for this |batch| in a |branch|. :param branch: the |branch| to get the stock balance for :returns: the amount of stock available in the |branch| """ store = self.store stock_items = store.find(ProductStockItem, storable=self.storable, batch=self, branch=branch) return stock_items.sum(ProductStockItem.quantity) or Decimal(0) # # IDescribable # def get_description(self): return self.batch_number
class CostCenterEntry(Domain): """A operation that generated some cost in a |costcenter|. A cost can be generated when a lonely out |payment| is paid or when some operations on the stock are performed. """ __storm_table__ = 'cost_center_entry' cost_center_id = IdCol() #: The cost center this entry belongs to cost_center = Reference(cost_center_id, 'CostCenter.id') payment_id = IdCol() #: The payment that generated this cost. payment = Reference(payment_id, 'Payment.id') stock_transaction_id = IdCol() #: The stock movement transaction that generated this cost. stock_transaction = Reference(stock_transaction_id, 'StockTransactionHistory.id')
class FiscalSaleHistory(Domain): """Holds fiscal information about the sales. """ TYPE_CPF = u'cpf' TYPE_CNPJ = u'cnpj' __storm_table__ = 'fiscal_sale_history' document_type = EnumCol(allow_none=False, default=TYPE_CPF) document = UnicodeCol(default=None) sale_id = IdCol() sale = Reference(sale_id, 'Sale.id') coo = IntCol(default=0) document_counter = IntCol(default=0)