def finish(self): for payment in self.model.group.payments: if payment.is_preview(): # Set payments created on SaleReturnPaymentStep as pending payment.set_pending() SaleReturnWizardFinishEvent.emit(self.model) total_amount = self.model.total_amount # If the user chose to create credit for the client instead of returning # money, there is no need to display this messages. if not self.credit: if total_amount == 0: info(_("The client does not have a debt to this sale anymore. " "Any existing unpaid installment will be cancelled.")) elif total_amount < 0: info(_("A reversal payment to the client will be created. " "You can see it on the Payable Application.")) self.model.return_(method_name=u'credit' if self.credit else u'money') self.retval = self.model self.close() if self.credit: if yesno(_(u'Would you like to print the credit letter?'), gtk.RESPONSE_YES, _(u"Print Letter"), _(u"Don't print")): print_report(ClientCreditReport, self.model.client)
def on_SalesCancel__activate(self, action): sale_view = self.results.get_selected() can_cancel = api.sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON') if can_cancel and ECFIsLastSaleEvent.emit(sale_view.sale): info(_("That is last sale in ECF. Return using the menu " "ECF - Cancel Last Document")) return store = api.new_store() sale = store.fetch(sale_view.sale) msg_text = _(u"This will cancel the sale, Are you sure?") model = SaleComment(store=store, sale=sale, author=api.get_current_user(store)) retval = self.run_dialog( NoteEditor, store, model=model, attr_name='comment', message_text=msg_text, label_text=_(u"Reason"), mandatory=True, ok_button_label=_(u"Cancel sale"), cancel_button_label=_(u"Don't cancel")) if not retval: store.rollback() return sale.cancel() store.commit(close=True) self.refresh()
def _add_sellable(self, sellable, batch=None): quantity = self._read_quantity() if quantity == 0: return if not sellable.is_valid_quantity(quantity): warning( _(u"You cannot sell fractions of this product. " u"The '%s' unit does not allow that") % sellable.unit_description) return if sellable.product: # If the sellable has a weight unit specified and we have a scale # configured for this station, go and check what the scale says. if (sellable and sellable.unit and sellable.unit.unit_index == UnitType.WEIGHT and self._scale_settings): self._read_scale(sellable) storable = sellable.product_storable if storable is not None: if not self._check_available_stock(storable, sellable): info( _("You cannot sell more items of product %s. " "The available quantity is not enough.") % sellable.get_description()) self.barcode.set_text('') self.barcode.grab_focus() return self._update_list(sellable, batch=batch) self.barcode.grab_focus()
def _change_status(self, work_order, new_status): with api.new_store() as store: work_order = store.fetch(work_order) reason = self._ask_reason(work_order, new_status) if reason is False: store.retval = False return # FIXME Redo this logic, it wont unset if the user go further back on # status if work_order.status == new_status: if work_order.client_informed_date: work_order.unset_client_informed(reason) return True try: work_order.change_status(new_status, reason) except InvalidStatus as e: info(str(e)) store.retval = False except NeedReason as e: info(str(e), _("Make the change on the order's menu so " "you can specify a reason")) store.retval = False return store.retval
def return_sale(parent, sale, store): from stoqlib.gui.wizards.salereturnwizard import SaleReturnWizard cancel_last_coupon = sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON') if cancel_last_coupon and ECFIsLastSaleEvent.emit(sale): info(_("That is last sale in ECF. Return using the menu " "ECF - Cancel Last Document")) return if sale.can_return(): need_document = not sysparam.get_bool('ACCEPT_SALE_RETURN_WITHOUT_DOCUMENT') if need_document and not sale.get_client_document(): warning(_('It is not possible to accept a returned sale from clients ' 'without document. Please edit the client document or change ' 'the sale client')) return returned_sale = sale.create_sale_return_adapter() retval = run_dialog(SaleReturnWizard, parent, store, returned_sale) elif sale.can_cancel(): retval = cancel_sale(sale) else: retval = False return retval
def _run_product_component_dialog(self, product_component=None): update = True if product_component is None: update = False component = self.component_combo.get_selected_data() product_component = TemporaryProductComponent( product=self._product, component=component) # If we try to add a component which is already in tree, # just edit it for component in self.component_tree: if component.component == product_component.component: update = True product_component = component break if not self._can_add_component(product_component): product_desc = self._product.sellable.get_description() component_desc = product_component.description info(_(u'You can not add this product as component, since ' '%s is composed by %s' % (component_desc, product_desc))) return toplevel = self.get_toplevel().get_toplevel() # We cant use savepoint here, since product_component # is not an ORM object. model = run_dialog(ProductComponentEditor, toplevel, self.store, product_component) if not model: return if update: self.component_tree.update(model) else: self._add_to_component_tree(model) self._update_widgets()
def _can_add_payment(self): if self.base_value.read() is ValueUnset: return False if self._outstanding_value <= 0: return False payments = self.model.group.get_valid_payments() payment_count = payments.find( Payment.method_id == self._method.id).count() if payment_count >= self._method.max_installments: info(_(u'You can not add more payments using the %s ' 'payment method.') % self._method.description) return False # If we are creaing out payments (PurchaseOrder) or the Sale does not # have a client, assume all options available are creatable. if (isinstance(self.model, (PurchaseOrder, StockDecrease)) or not self.model.client): return True method_values = {self._method: self._holder.value} for i, payment in enumerate(self.model.group.payments): method_values.setdefault(payment.method, 0) method_values[payment.method] += payment.value for method, value in method_values.items(): try: self.model.client.can_purchase(method, value) except SellError as e: warning(str(e)) return False return True
def _cancel_last_document(self): try: self._validate_printer() except DeviceError as e: warning(str(e)) return store = new_store() last_doc = self._get_last_document(store) if not self._confirm_last_document_cancel(last_doc): store.close() return if last_doc.last_till_entry: self._cancel_last_till_entry(last_doc, store) elif last_doc.last_sale: # Verify till balance before cancel the last sale. till = Till.get_current(store) if last_doc.last_sale.total_amount > till.get_balance(): warning(_("You do not have this value on till.")) store.close() return cancelled = self._printer.cancel() if not cancelled: info(_("Cancelling sale failed, nothing to cancel")) store.close() return else: self._cancel_last_sale(last_doc, store) store.commit()
def _check_branch(self): from stoqlib.database.runtime import (get_default_store, new_store, get_current_station, set_current_branch_station) from stoqlib.domain.person import Company from stoqlib.lib.parameters import sysparam from stoqlib.lib.message import info default_store = get_default_store() compaines = default_store.find(Company) if (compaines.count() == 0 or not sysparam.has_object('MAIN_COMPANY')): from stoqlib.gui.base.dialogs import run_dialog from stoqlib.gui.dialogs.branchdialog import BranchDialog if self._ran_wizard: info(_("You need to register a company before start using Stoq")) else: info(_("Could not find a company. You'll need to register one " "before start using Stoq")) store = new_store() person = run_dialog(BranchDialog, None, store) if not person: raise SystemExit branch = person.branch sysparam.set_object(store, 'MAIN_COMPANY', branch) current_station = get_current_station(store) if current_station is not None: current_station.branch = branch store.commit() store.close() set_current_branch_station(default_store, station_name=None)
def _open_sintegra(self): branch = api.get_current_branch(self.app.store) if branch.manager is None: info(_("You must define a manager to this branch before you can create" " a sintegra archive")) return self.app.run_dialog(SintegraDialog, self.app.store)
def apply_patch(store): info(u'The schema update might take a long time to complete, depending ' 'the size of your database and your hardware.') for payment in store.find(Payment).order_by(['due_date', 'paid_date', 'cancel_date']): if payment.is_preview(): continue if payment.due_date: history_date = payment.due_date.date() else: history_date = payment.open_date.date() PaymentFlowHistory.add_payment(store, payment, history_date) if payment.is_paid(): PaymentFlowHistory.add_paid_payment(store, payment, payment.paid_date.date()) elif payment.is_cancelled(): if payment.paid_date: PaymentFlowHistory.add_paid_payment(store, payment, payment.paid_date.date()) PaymentFlowHistory.remove_paid_payment(store, payment, payment.cancel_date.date()) else: PaymentFlowHistory.remove_payment(store, payment, payment.due_date.date())
def _set_done(self): # XXX: Better text here info(_("Stoq.Link registration successful! You may " "manage your installation from there now")) self._done = True self.confirm() self._set_processing(False)
def validate_confirm(self): try: self.model.add_lost(self.quantity.read()) except (ValueError, AssertionError): info(_(u"Can not lose this quantity. Not enough materials " "allocated to this production.")) return False return True
def next_step(self): self.wizard.all_products = self.all_products.get_active() if self.wizard.is_for_another_branch() and self.model.identifier > 0: info(_('The identifier for this purchase will be defined when it ' 'is synchronized to the detination branch')) self.model.identifier = self.wizard.temporary_identifier return PurchaseItemStep(self.wizard, self, self.store, self.model)
def post_init(self): self.register_validate_function(self._validation_func) self.force_validation() missing_value = self.slave.get_missing_change_value() if missing_value < 0: info(_(u"Your payments total is greater than the sale total. Maybe" " you want to correct them."))
def print_cheques_for_payment_group(store, group): """ Given a instance that implements the PaymentGroup interface, iterate over all its items printing a cheque for them. """ payments = group.get_valid_payments() printer = get_current_cheque_printer_settings(store) if not printer: return printer_banks = printer.get_banks() current_branch = get_current_branch(store) main_address = current_branch.person.get_main_address() if not main_address: raise ValueError("The cheque can not be printed since there is no " "main address defined for the current branch.") max_len = printer.get_capability("cheque_city").max_len city = main_address.city_location.city[:max_len] for idx, payment in enumerate(payments): if payment.method.method_name == 'check': continue check_data = payment.method.operation.get_check_data_by_payment( payment) bank_id = check_data.bank_data.bank_id try: bank = printer_banks[bank_id] except KeyError: continue thirdparty = group.recipient info(_(u"Insert Cheque %d") % (idx + 1)) max_len = printer.get_capability("cheque_thirdparty").max_len thirdparty = thirdparty and thirdparty.name[:max_len] or "" printer.print_cheque(bank, payment.value, thirdparty, city)
def _update_list(self, sellable): assert isinstance(sellable, Sellable) try: sellable.check_taxes_validity() except TaxError as strerr: # If the sellable icms taxes are not valid, we cannot sell it. warning(strerr) return quantity = self.sellableitem_proxy.model.quantity is_service = sellable.service if is_service and quantity > 1: # It's not a common operation to add more than one item at # a time, it's also problematic since you'd have to show # one dialog per service item. See #3092 info(_("It's not possible to add more than one service " "at a time to an order. So, only one was added.")) sale_item = TemporarySaleItem(sellable=sellable, quantity=quantity) if is_service: with api.trans() as store: rv = self.run_dialog(ServiceItemEditor, store, sale_item) if not rv: return self._update_added_item(sale_item)
def checkout(self, cancel_clear=False): """Initiates the sale wizard to confirm sale. :param cancel_clear: If cancel_clear is true, the sale will be cancelled if the checkout is cancelled. """ assert len(self.sale_items) >= 1 if self._current_store: store = self._current_store savepoint = 'before_run_fiscalprinter_confirm' store.savepoint(savepoint) else: store = api.new_store() savepoint = None if self._trade: if self._get_subtotal() < self._trade.returned_total: info(_("Traded value is greater than the new sale's value. " "Please add more items or return it in Sales app, " "then make a new sale")) return sale = self._create_sale(store) self._trade.new_sale = sale self._trade.trade() else: sale = self._create_sale(store) if sysparam.get_bool('CONFIRM_SALES_ON_TILL'): sale.order() store.commit() else: assert self._coupon ordered = self._coupon.confirm(sale, store, savepoint, subtotal=self._get_subtotal()) # Dont call store.confirm() here, since coupon.confirm() # above already did it if not ordered: # FIXME: Move to TEF plugin manager = get_plugin_manager() if manager.is_active('tef') or cancel_clear: self._cancel_order(show_confirmation=False) elif not self._current_store: # Just do that if a store was created above and # if _cancel_order wasn't called (it closes the connection) store.rollback(close=True) return log.info("Checking out") self._coupon = None POSConfirmSaleEvent.emit(sale, self.sale_items[:]) # We must close the connection only after the event is emmited, since it # may use value from the sale that will become invalid after it is # closed store.close() self._clear_order()
def validate_confirm(self): try: self.model.allocate(self.quantity.read()) except (ValueError, AssertionError): info(_(u'Can not allocate this quantity.')) return False return True
def _setup_widgets(self): self.quoting_list.set_columns(self._get_columns()) self._populate_quoting_list() if not len(self.quoting_list) > 0: info(_(u'No supplier have been found for any of the selected ' 'items.\nThis quote will be cancelled.')) self.wizard.finish()
def _on_SaleAvoidCancel(self, sale): if not sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON'): return False if self._is_ecf_last_sale(sale): info(_("That is last sale in ECF. Return using the menu " "ECF - Cancel Last Document")) return True return False
def _on_coupon__cancel(self, coupon): if coupon.closed: # In this case, the Sale and TillEntries will be rolled back by # fiscalprinter. We only need to cancel the last coupon on the ecf if not self._printer.cancel_last_coupon(): info(_("Coupon cancellation failed...")) else: coupon.cancel()
def _open_invoice_printers(self): if self.app.store.find(InvoiceLayout).is_empty(): info(_("You must create at least one invoice layout " "before adding an invoice printer")) return store = api.new_store() model = self.app.run_dialog(InvoicePrinterDialog, store) store.confirm(model) store.close()
def validate_confirm(self): query = self._get_sellables_query() sellables = Inventory.get_sellables_for_inventory(self.store, self.model.branch, query) if sellables.is_empty(): info(_(u'No products have been found in the selected ' 'categories.')) return False return True
def validate_confirm(self): # This is a generator. It'll be evaluated to True # even if it's len should be 0. Use a list for comparison instead. if not list(self._get_sellables()): info(_(u'No products have been found in the selected ' 'categories.')) return False return True
def _cancel_last_sale(self, last_doc, store): if last_doc.last_sale.status == Sale.STATUS_RETURNED: return sale = store.fetch(last_doc.last_sale) returned_sale = sale.create_sale_return_adapter() returned_sale.reason = _(u"Cancelling last document on ECF") returned_sale.return_() last_doc.last_sale = None info(_("Document was cancelled"))
def delete_model(self, model, store): sellables = store.find(Sellable, tax_constant=model) quantity = sellables.count() if quantity > 0: msg = _(u"You can't remove this tax, since %d products or " "services are taxed with '%s'.") % (quantity, model.get_description()) info(msg) else: store.remove(model)
def validate_confirm(self): if self.document.is_empty(): if self._initial_document_type == FiscalSaleHistory.TYPE_CPF: doc = 'CPF' else: doc = 'CNPJ' info(_("The %s cannot be empty") % doc) return False return True
def on_confirm(self): # We are using this hook as a callback for the OK button productions = [p.obj for p in self.productions if p.selected] store = api.new_store() group = self.model.create_quote_group(productions, store) store.confirm(group) store.close() info(_(u'The quote group was succesfully created and it is available ' 'in the Purchase application.')) self.retval = group
def _populate_ecf_printer(self, status): serial = unicode(status.printer.get_serial()) if self.store.find(ECFPrinter, device_serial=serial): status.stop() status.get_port().close() info(_("This printer is already known to the system")) return False self.model.device_serial = serial self._populate_constants(self.model, status) return True
def validate_confirm(self): if not len(self.component_tree) > 0: info(_(u'There is no component in this product.')) return False return True
def _open_stoq_link_connect(self): if stoq.trial_mode: return info(_('Online features are not available in trial mode')) self.app.run_dialog(PinDialog, self.app.store)
def confirm(self, *args): self.store.confirm(True) BasicDialog.confirm(self, *args) info(_("Changes will be applied after all instances of Stoq are restarted."))
def update(self, plugins=True, backup=True): log.info("Upgrading database (plugins=%r, backup=%r)" % (plugins, backup)) try: log.info("Locking database") self.default_store.lock_database() except DatabaseError: msg = _( 'Could not lock database. This means there are other clients ' 'connected. Make sure to close every Stoq client ' 'before updating the database') error(msg) # Database migration is actually run in subprocesses, We need to unlock # the tables again and let the upgrade continue log.info("Releasing database lock") self.default_store.unlock_database() sucess = db_settings.test_connection() if not sucess: # FIXME: Improve this message after 1.5 is released msg = _(u'Could not connect to the database using command line ' 'tool! Aborting.') + ' ' msg += _(u'Please, check if you can connect to the database ' 'using:') + ' ' msg += _(u'psql -l -h <server> -p <port> -U <username>') error(msg) return if backup: temporary = tempfile.mktemp(prefix="stoq-dump-") log.info("Making a backup to %s" % (temporary, )) create_log.info("BACKUP-START:") success = db_settings.dump_database(temporary) if not success: info(_(u'Could not create backup! Aborting.')) info(_(u'Please contact stoq team to inform this problem.\n')) return # We have to wrap a try/except statement inside a try/finally to # support python previous to 2.5 version. try: try: super(StoqlibSchemaMigration, self).update() if plugins: self.update_plugins() except Exception: exc = sys.exc_info() tb_str = ''.join(traceback.format_exception(*exc)) collect_traceback(exc, submit=True) create_log.info("ERROR:%s" % (tb_str, )) if backup: log.info("Restoring backup %s" % (temporary, )) create_log.info("RESTORE-START:") new_name = db_settings.restore_database(temporary) create_log.info("RESTORE-DONE:%s" % (new_name, )) return False finally: if backup is True: os.unlink(temporary) log.info("Migration done") return True
def _printer_status__timeout(self, status): self.progress_dialog.stop() info( _("Could not find a %s printer connected to %s") % (self.model.model_name, status.get_device_name()))
def edit_item(self, item): if item.brand == 'virtual': info(_("Cant edit a virtual printer")) return False return ModelListSlave.edit_item(self, item)
def validate_confirm(self): if not self._is_valid_cost(self.cost.read()): info(self._cost_msg) return False return True
def on_ExportFilizola__activate(self, action): dest = generate_filizola_file(self.store) info(_('File saved in %s') % dest)