def test_get_serial_devices(self, comports):
        class MockDevice:
            device = '/dev/ttyS0'
        comports.return_value = [MockDevice()]
        devices = DeviceManager.get_serial_devices()

        self.assertEqual(len(devices), 1)
        self.assertEqual(devices[0].device_name, '/dev/ttyS0')
Exemple #2
0
    def test_get_serial_devices(self, comports):
        class MockDevice:
            device = '/dev/ttyS0'

        comports.return_value = [MockDevice()]
        devices = DeviceManager.get_serial_devices()

        self.assertEqual(len(devices), 1)
        self.assertEqual(devices[0].device_name, '/dev/ttyS0')
Exemple #3
0
    def _populate_serial_ports(self):
        values = []
        for device in DeviceManager.get_serial_devices():
            values.append(device.device_name)
        if not self.model.device_name in values:
            values.append(self.model.device_name)
        if sysparam.get_bool('DEMO_MODE') or is_developer_mode():
            values.append(u'/dev/null')

        self.device_name.prefill(values)
Exemple #4
0
    def setup_device_port_combo(self):
        items = [(_("Choose..."), None)]
        items.extend([(str(device.device_name), str(device.device_name))
                      for device in DeviceManager.get_serial_devices()])
        items.extend(self._get_usb_devices())

        if is_developer_mode():
            # Include virtual port for virtual printer
            items.append(('Virtual device', u'/dev/null'))

        devices = [i[1] for i in items]
        if self.model.device_name not in devices:
            items.append(('Unkown device (%s)' % self.model.device_name,
                          self.model.device_name))
        self.device_combo.prefill(items)
Exemple #5
0
class ECFEditor(BaseEditor):
    translation_domain = 'stoq'
    domain = 'ecf'
    gladefile = 'FiscalPrinterDialog'
    model_type = ECFPrinter
    model_name = _('Fiscal Printer')
    proxy_widgets = [
        'device_name', 'device_serial', 'is_active', 'baudrate', 'user_number',
        'register_date', 'register_cro'
    ]

    def __init__(self, store, model=None):
        self._device_manager = DeviceManager()
        BaseEditor.__init__(self, store, model)
        self.progress_dialog = ProgressDialog()
        self.progress_dialog.connect('cancel',
                                     self._on_progress_dialog__cancel)
        self.progress_dialog.set_transient_for(self.main_dialog)

        if self.edit_mode:
            self.printer.set_sensitive(False)
            self.main_dialog.ok_button.grab_focus()
        else:
            self.edit_constants.hide()
            self.device_serial.hide()
            self.device_serial_label.hide()
            self.is_active.hide()

    #
    # BaseEditor
    #

    def create_model(self, store):
        model = ECFPrinter(brand=u'daruma',
                           model=u'FS345',
                           device_name=u'/dev/ttyS0',
                           device_serial=u'',
                           baudrate=9600,
                           station=get_current_station(store),
                           is_active=True,
                           store=store)
        if platform.system() == 'Windows':
            model.device_name = u'COM1'
        return model

    def setup_proxies(self):
        self._populate_printers()
        self._populate_serial_ports()
        self._populate_baudrate()
        self.proxy = self.add_proxy(self.model, ECFEditor.proxy_widgets)
        self.printer.select_item_by_label(self.model.get_description())

    def validate_confirm(self):
        if not self.can_activate_printer():
            return False

        if self.edit_mode:
            return True
        try:
            self._status = ECFAsyncPrinterStatus(self.model.device_name,
                                                 self.model.printer_class,
                                                 self.model.baudrate)
        except SerialException as e:
            warning(_('Error opening serial port'), str(e))
            return False
        self._status.connect('reply', self._printer_status__reply)
        self._status.connect('timeout', self._printer_status__timeout)
        self.progress_dialog.set_label(
            _("Probing for a %s printer on %s") %
            (self.model.model_name, self._status.get_device_name()))
        self.progress_dialog.start()
        return False

    def can_activate_printer(self):
        serial = self.model.device_serial
        printers = self.store.find(ECFPrinter,
                                   is_active=True,
                                   station=get_current_station(self.store))
        till = self.store.find(Till,
                               status=Till.STATUS_OPEN,
                               station=get_current_station(self.store)).one()
        if till and printers:
            warning(
                _("You need to close the till opened at %s before "
                  "changing this printer.") % till.opening_date.date())
            return False
        for p in printers:
            if p.device_serial != serial and self.model.is_active:
                warning(
                    _(u'The ECF %s is already active for this '
                      'station. Deactivate that printer before '
                      'activating this one.') % p.model)
                return False
        return True

    #
    # Callbacks
    #

    def _on_progress_dialog__cancel(self, progress):
        # FIXME:
        # status.stop()
        pass

    def on_printer__content_changed(self, combo):
        # Cannot change the model in edit mode!
        if self.edit_mode:
            return
        printer = combo.get_selected()
        self.model.model = printer.model
        self.model.brand = printer.brand

        # These are not persistent
        self.model.model_name = printer.model_name
        self.model.printer_class = printer.printer_class

    def on_edit_constants__clicked(self, button):
        run_dialog(DeviceConstantsDialog, self, self.store, self.model)

    def _printer_status__reply(self, status, reply):
        self.progress_dialog.stop()
        if not reply:
            return
        if not self._populate_ecf_printer(status):
            return

        if yesno(
                _("An ECF Printer was added. You need to restart Stoq "
                  "before using it. Would you like to restart it now?"),
                gtk.RESPONSE_YES, _("Restart now"), _("Restart later")):
            self.store.commit()
            raise SystemExit

        # FIXME: move to base dialogs or base editor
        self.retval = self.model
        self.main_dialog.close()

    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()))

    #
    # Private
    #

    def _populate_baudrate(self):
        values = get_baudrate_values()
        self.baudrate.prefill(values)

    def _populate_printers(self):
        supported_ifaces = get_supported_printers_by_iface(
            ICouponPrinter).items()
        printers = []
        for brand, printer_classes in supported_ifaces:
            for printer_class in printer_classes:
                printer = _PrinterModel(brand, printer_class)
                printers.append((printer.get_description(), printer))

        # Allow to use virtual printer for both demo mode and developer mode
        # so it's easier for testers and developers to test ecf functionality
        if sysparam.get_bool('DEMO_MODE') or is_developer_mode():
            from stoqdrivers.printers.virtual.Simple import Simple
            printer = _PrinterModel('virtual', Simple)
            printers.append((printer.get_description(), printer))

        self.printer.prefill(
            locale_sorted(printers, key=operator.itemgetter(0)))

    def _populate_serial_ports(self):
        values = []
        for device in self._device_manager.get_serial_devices():
            values.append(device.device_name)
        if not self.model.device_name in values:
            values.append(self.model.device_name)
        self.device_name.prefill(values)

    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 _populate_constants(self, model, status):
        driver = status.get_driver()
        for tax_enum, device_value, value in driver.get_tax_constants():
            if tax_enum == TaxType.CUSTOM:
                constant = self.store.find(SellableTaxConstant,
                                           tax_value=value).one()
                # If the constant is not defined in the system, create it
                if not constant:
                    constant = SellableTaxConstant(
                        tax_value=value,
                        tax_type=int(TaxType.CUSTOM),
                        description=u'%0.2f %%' % value,
                        store=self.store)
            elif tax_enum == TaxType.SERVICE:
                constant = self.store.find(DeviceConstant,
                                           constant_enum=int(tax_enum),
                                           printer=model).one()
                # Skip, If we have a service tax defined for this printer
                # This needs to be improved when we support more than one
                # service tax
                if constant is not None:
                    continue
            else:
                constant = self.store.find(SellableTaxConstant,
                                           tax_type=int(tax_enum)).one()
                # Ignore if its unkown tax
                if not constant:
                    continue

            if value:
                constant_name = u'%0.2f %%' % (value, )
            elif constant:
                constant_name = constant.description
            else:
                constant_name = None
            DeviceConstant(constant_enum=int(tax_enum),
                           constant_name=constant_name,
                           constant_type=DeviceConstant.TYPE_TAX,
                           constant_value=value,
                           device_value=device_value,
                           printer=model,
                           store=self.store)

        # This is going to be ugly, most printers don't support
        # a real constant for the payment methods, so we have to look
        # at the description and guess
        payment_enums = {
            'dinheiro': PaymentMethodType.MONEY,
            'cheque': PaymentMethodType.CHECK,
            'boleto': PaymentMethodType.BILL,
            'cartao credito': PaymentMethodType.CREDIT_CARD,
            'cartao debito': PaymentMethodType.DEBIT_CARD,
            'financeira': PaymentMethodType.FINANCIAL,
            'vale compra': PaymentMethodType.GIFT_CERTIFICATE
        }

        payment_methods = []
        for device_value, constant_name in driver.get_payment_constants():
            lower = constant_name.lower()
            lower = lower.replace('é', 'e')  # Workaround method names with
            lower = lower.replace('ã', 'a')  # accents
            payment_enum = payment_enums.get(lower)
            if payment_enum is None:
                continue

            # Avoid register the same method twice for the same device
            if payment_enum in payment_methods:
                continue
            DeviceConstant(constant_enum=int(payment_enum),
                           constant_name=unicode(constant_name),
                           constant_type=DeviceConstant.TYPE_PAYMENT,
                           constant_value=None,
                           device_value=device_value,
                           printer=model,
                           store=self.store)
            payment_methods.append(payment_enum)
Exemple #6
0
class ECFEditor(BaseEditor):
    translation_domain = 'stoq'
    domain = 'ecf'
    gladefile = 'FiscalPrinterDialog'
    model_type = ECFPrinter
    model_name = _('Fiscal Printer')
    proxy_widgets = ['device_name', 'device_serial', 'is_active', 'baudrate',
                     'user_number', 'register_date', 'register_cro']

    def __init__(self, store, model=None):
        self._device_manager = DeviceManager()
        BaseEditor.__init__(self, store, model)
        self.progress_dialog = ProgressDialog()
        self.progress_dialog.connect('cancel',
                                     self._on_progress_dialog__cancel)
        self.progress_dialog.set_transient_for(self.main_dialog)

        if self.edit_mode:
            self.printer.set_sensitive(False)
            self.main_dialog.ok_button.grab_focus()
        else:
            self.edit_constants.hide()
            self.device_serial.hide()
            self.device_serial_label.hide()
            self.is_active.hide()

    #
    # BaseEditor
    #

    def create_model(self, store):
        model = ECFPrinter(brand=u'daruma',
                           model=u'FS345',
                           device_name=u'/dev/ttyS0',
                           device_serial=u'',
                           baudrate=9600,
                           station=get_current_station(store),
                           is_active=True,
                           store=store)
        if platform.system() == 'Windows':
            model.device_name = u'COM1'
        return model

    def setup_proxies(self):
        self._populate_printers()
        self._populate_serial_ports()
        self._populate_baudrate()
        self.proxy = self.add_proxy(self.model,
                                    ECFEditor.proxy_widgets)
        self.printer.select_item_by_label(self.model.get_description())

    def validate_confirm(self):
        if not self.can_activate_printer():
            return False

        if self.edit_mode:
            return True
        try:
            self._status = ECFAsyncPrinterStatus(self.model.device_name,
                                                 self.model.printer_class,
                                                 self.model.baudrate)
        except SerialException as e:
            warning(_('Error opening serial port'), str(e))
            return False
        self._status.connect('reply', self._printer_status__reply)
        self._status.connect('timeout', self._printer_status__timeout)
        self.progress_dialog.set_label(_("Probing for a %s printer on %s") % (
            self.model.model_name, self._status.get_device_name()))
        self.progress_dialog.start()
        return False

    def can_activate_printer(self):
        serial = self.model.device_serial
        printers = self.store.find(ECFPrinter, is_active=True,
                                   station=get_current_station(self.store))
        till = self.store.find(Till, status=Till.STATUS_OPEN,
                               station=get_current_station(self.store)).one()
        if till and printers:
            warning(_("You need to close the till opened at %s before "
                      "changing this printer.") % till.opening_date.date())
            return False
        for p in printers:
            if p.device_serial != serial and self.model.is_active:
                warning(_(u'The ECF %s is already active for this '
                          'station. Deactivate that printer before '
                          'activating this one.') % p.model)
                return False
        return True

    #
    # Callbacks
    #

    def _on_progress_dialog__cancel(self, progress):
        # FIXME:
        # status.stop()
        pass

    def on_printer__content_changed(self, combo):
        # Cannot change the model in edit mode!
        if self.edit_mode:
            return
        printer = combo.get_selected()
        self.model.model = printer.model
        self.model.brand = printer.brand

        # These are not persistent
        self.model.model_name = printer.model_name
        self.model.printer_class = printer.printer_class

    def on_edit_constants__clicked(self, button):
        run_dialog(DeviceConstantsDialog, self, self.store, self.model)

    def _printer_status__reply(self, status, reply):
        self.progress_dialog.stop()
        if not reply:
            return
        if not self._populate_ecf_printer(status):
            return

        if yesno(_("An ECF Printer was added. You need to restart Stoq "
                   "before using it. Would you like to restart it now?"),
                 gtk.RESPONSE_YES, _("Restart now"), _("Restart later")):
            self.store.commit()
            raise SystemExit

        # FIXME: move to base dialogs or base editor
        self.retval = self.model
        self.main_dialog.close()

    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()))

    #
    # Private
    #

    def _populate_baudrate(self):
        values = get_baudrate_values()
        self.baudrate.prefill(values)

    def _populate_printers(self):
        supported_ifaces = get_supported_printers_by_iface(ICouponPrinter).items()
        printers = []
        for brand, printer_classes in supported_ifaces:
            for printer_class in printer_classes:
                printer = _PrinterModel(brand, printer_class)
                printers.append((printer.get_description(), printer))

        # Allow to use virtual printer for both demo mode and developer mode
        # so it's easier for testers and developers to test ecf functionality
        if sysparam.get_bool('DEMO_MODE') or is_developer_mode():
            from stoqdrivers.printers.virtual.Simple import Simple
            printer = _PrinterModel('virtual', Simple)
            printers.append((printer.get_description(), printer))

        self.printer.prefill(locale_sorted(
            printers, key=operator.itemgetter(0)))

    def _populate_serial_ports(self):
        values = []
        for device in self._device_manager.get_serial_devices():
            values.append(device.device_name)
        if not self.model.device_name in values:
            values.append(self.model.device_name)
        self.device_name.prefill(values)

    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 _populate_constants(self, model, status):
        driver = status.get_driver()
        for tax_enum, device_value, value in driver.get_tax_constants():
            if tax_enum == TaxType.CUSTOM:
                constant = self.store.find(SellableTaxConstant,
                                           tax_value=value).one()
                # If the constant is not defined in the system, create it
                if not constant:
                    constant = SellableTaxConstant(tax_value=value,
                                                   tax_type=int(TaxType.CUSTOM),
                                                   description=u'%0.2f %%' % value,
                                                   store=self.store)
            elif tax_enum == TaxType.SERVICE:
                constant = self.store.find(DeviceConstant,
                                           constant_enum=int(tax_enum),
                                           printer=model).one()
                # Skip, If we have a service tax defined for this printer
                # This needs to be improved when we support more than one
                # service tax
                if constant is not None:
                    continue
            else:
                constant = self.store.find(SellableTaxConstant,
                                           tax_type=int(tax_enum)).one()
                # Ignore if its unkown tax
                if not constant:
                    continue

            if value:
                constant_name = u'%0.2f %%' % (value, )
            elif constant:
                constant_name = constant.description
            else:
                constant_name = None
            DeviceConstant(constant_enum=int(tax_enum),
                           constant_name=constant_name,
                           constant_type=DeviceConstant.TYPE_TAX,
                           constant_value=value,
                           device_value=device_value,
                           printer=model,
                           store=self.store)

        # This is going to be ugly, most printers don't support
        # a real constant for the payment methods, so we have to look
        # at the description and guess
        payment_enums = {'dinheiro': PaymentMethodType.MONEY,
                         'cheque': PaymentMethodType.CHECK,
                         'boleto': PaymentMethodType.BILL,
                         'cartao credito': PaymentMethodType.CREDIT_CARD,
                         'cartao debito': PaymentMethodType.DEBIT_CARD,
                         'financeira': PaymentMethodType.FINANCIAL,
                         'vale compra': PaymentMethodType.GIFT_CERTIFICATE
                         }

        payment_methods = []
        for device_value, constant_name in driver.get_payment_constants():
            lower = constant_name.lower()
            lower = lower.replace('é', 'e')  # Workaround method names with
            lower = lower.replace('ã', 'a')  # accents
            payment_enum = payment_enums.get(lower)
            if payment_enum is None:
                continue

            # Avoid register the same method twice for the same device
            if payment_enum in payment_methods:
                continue
            DeviceConstant(constant_enum=int(payment_enum),
                           constant_name=unicode(constant_name),
                           constant_type=DeviceConstant.TYPE_PAYMENT,
                           constant_value=None,
                           device_value=device_value,
                           printer=model,
                           store=self.store)
            payment_methods.append(payment_enum)
class DeviceSettingsEditor(BaseEditor):
    gladefile = 'DeviceSettingsEditor'
    model_type = DeviceSettings
    proxy_widgets = ('type_combo',
                     'brand_combo',
                     'device_combo',
                     'model_combo',
                     'station',
                     'is_active_button')

    def __init__(self, store, model=None, station=None):
        if station is not None and not isinstance(station, BranchStation):
            raise TypeError("station should be a BranchStation")
        self._device_manager = DeviceManager()
        self.printers_dict = get_supported_printers()
        self._branch_station = station
        # This attribute is set to True when setup_proxies is finished
        self._is_initialized = False
        BaseEditor.__init__(self, store, model)
        self._original_brand = self.model.brand
        self._original_model = self.model.model

    def refresh_ok(self, *args):
        if self._is_initialized:
            BaseEditor.refresh_ok(self, self.model.is_valid())

    def setup_station_combo(self):
        if self._branch_station:
            self.station.prefill([(self._branch_station.name,
                                   self._branch_station)])
            self.model.station = self._branch_station
            self.station.set_sensitive(False)
            return
        self.station.prefill(
            [(station.name, station)
             for station in self.store.find(BranchStation)])

    def setup_device_port_combo(self):
        items = [(_("Choose..."), None)]
        items.extend([(unicode(device.device_name), unicode(device.device_name))
                      for device in self._device_manager.get_serial_devices()])
        self.device_combo.prefill(items)

    def setup_device_types_combo(self):
        items = [(_("Choose..."), None)]
        device_types = (
            # TODO: Reenable when we have cheque printers working.
            # DeviceSettings.CHEQUE_PRINTER_DEVICE,
            DeviceSettings.SCALE_DEVICE, )
        items.extend([(self.model.get_device_type_name(t), t)
                      for t in device_types])
        self.type_combo.prefill(items)

    def setup_widgets(self):
        self.setup_device_types_combo()
        self.setup_device_port_combo()
        self.setup_station_combo()
        if not self.edit_mode:
            self.is_active_button.set_sensitive(False)

    def _get_supported_types(self):
        if self.model.type == DeviceSettings.SCALE_DEVICE:
            supported_types = get_supported_scales()
        elif self.model.type == DeviceSettings.CHEQUE_PRINTER_DEVICE:
            supported_types = get_supported_printers_by_iface(IChequePrinter)
        else:
            raise TypeError("The selected device type isn't supported")
        return supported_types

    def _get_supported_brands(self):
        return list(self._get_supported_types().keys())

    def _get_supported_models(self):
        return self._get_supported_types()[self.model.brand]

    def update_brand_combo(self):
        self.brand_combo.clear()
        self.brand_combo.set_sensitive(self.model.type is not None)
        if self.model.type is None:
            return
        items = [(_("Choose..."), None)]
        supported_brands = self._get_supported_brands()
        items.extend([(brand.capitalize(), unicode(brand))
                      for brand in supported_brands])
        self.brand_combo.prefill(items)

    def update_model_combo(self):
        self.model_combo.clear()
        brand = self.model.brand
        self.model_combo.set_sensitive(brand is not None)
        if self.model.brand is None:
            return
        supported_models = self._get_supported_models()
        items = [(_("Choose..."), None)]
        items.extend([(obj.model_name, unicode(obj.__name__))
                      for obj in supported_models])
        self.model_combo.prefill(items)

    #
    # BaseEditor hooks
    #

    def setup_proxies(self):
        self.setup_widgets()
        self.proxy = self.add_proxy(self.model,
                                    DeviceSettingsEditor.proxy_widgets)
        self._is_initialized = True

    def create_model(self, store):
        return DeviceSettings(device_name=None,
                              station=api.get_current_station(store),
                              brand=None,
                              model=None,
                              type=None,
                              store=store)

    def get_title(self, *args):
        if self.edit_mode:
            return _("Edit Device for %s") % self.model.station.name
        else:
            return _("Add Device")

    def validate_confirm(self):
        if not self.edit_mode:
            settings = DeviceSettings.get_by_station_and_type(
                store=api.get_default_store(),
                station=self.model.station.id,
                type=self.model.type)
            if settings:
                self.station.set_invalid(
                    _(u"A %s already exists for station \"%s\"") % (
                        self.model.get_device_type_name(),
                        self.model.station.name))
                return False
        return True

    #
    # Kiwi callbacks
    #

    def on_brand_combo__changed(self, *args):
        self.update_model_combo()
        self.refresh_ok()

    def on_type_combo__changed(self, *args):
        self.update_brand_combo()
        self.refresh_ok()

    def on_brand_combo__state_changed(self, *args):
        self.update_model_combo()
        self.refresh_ok()

    def on_model_combo__changed(self, *args):
        self.refresh_ok()

    def on_device_combo__changed(self, *args):
        self.refresh_ok()
Exemple #8
0
class DeviceSettingsEditor(BaseEditor):
    gladefile = 'DeviceSettingsEditor'
    model_type = DeviceSettings
    proxy_widgets = ('type_combo', 'brand_combo', 'device_combo',
                     'model_combo', 'station', 'is_active_button')

    def __init__(self, store, model=None, station=None):
        if station is not None and not isinstance(station, BranchStation):
            raise TypeError("station should be a BranchStation")
        self._device_manager = DeviceManager()
        self.printers_dict = get_supported_printers()
        self._branch_station = station
        # This attribute is set to True when setup_proxies is finished
        self._is_initialized = False
        BaseEditor.__init__(self, store, model)
        self._original_brand = self.model.brand
        self._original_model = self.model.model

    def refresh_ok(self, *args):
        if self._is_initialized:
            BaseEditor.refresh_ok(self, self.model.is_valid())

    def setup_station_combo(self):
        if self._branch_station:
            self.station.prefill([(self._branch_station.name,
                                   self._branch_station)])
            self.model.station = self._branch_station
            self.station.set_sensitive(False)
            return
        self.station.prefill([(station.name, station)
                              for station in self.store.find(BranchStation)])

    def setup_device_port_combo(self):
        items = [(_("Choose..."), None)]
        items.extend([(unicode(device.device_name),
                       unicode(device.device_name))
                      for device in self._device_manager.get_serial_devices()])
        self.device_combo.prefill(items)

    def setup_device_types_combo(self):
        items = [(_("Choose..."), None)]
        device_types = (
            # TODO: Reenable when we have cheque printers working.
            # DeviceSettings.CHEQUE_PRINTER_DEVICE,
            DeviceSettings.SCALE_DEVICE, )
        items.extend([(self.model.get_device_type_name(t), t)
                      for t in device_types])
        self.type_combo.prefill(items)

    def setup_widgets(self):
        self.setup_device_types_combo()
        self.setup_device_port_combo()
        self.setup_station_combo()
        if not self.edit_mode:
            self.is_active_button.set_sensitive(False)

    def _get_supported_types(self):
        if self.model.type == DeviceSettings.SCALE_DEVICE:
            supported_types = get_supported_scales()
        elif self.model.type == DeviceSettings.CHEQUE_PRINTER_DEVICE:
            supported_types = get_supported_printers_by_iface(IChequePrinter)
        else:
            raise TypeError("The selected device type isn't supported")
        return supported_types

    def _get_supported_brands(self):
        return list(self._get_supported_types().keys())

    def _get_supported_models(self):
        return self._get_supported_types()[self.model.brand]

    def update_brand_combo(self):
        self.brand_combo.clear()
        self.brand_combo.set_sensitive(self.model.type is not None)
        if self.model.type is None:
            return
        items = [(_("Choose..."), None)]
        supported_brands = self._get_supported_brands()
        items.extend([(brand.capitalize(), unicode(brand))
                      for brand in supported_brands])
        self.brand_combo.prefill(items)

    def update_model_combo(self):
        self.model_combo.clear()
        brand = self.model.brand
        self.model_combo.set_sensitive(brand is not None)
        if self.model.brand is None:
            return
        supported_models = self._get_supported_models()
        items = [(_("Choose..."), None)]
        items.extend([(obj.model_name, unicode(obj.__name__))
                      for obj in supported_models])
        self.model_combo.prefill(items)

    #
    # BaseEditor hooks
    #

    def setup_proxies(self):
        self.setup_widgets()
        self.proxy = self.add_proxy(self.model,
                                    DeviceSettingsEditor.proxy_widgets)
        self._is_initialized = True

    def create_model(self, store):
        return DeviceSettings(device_name=None,
                              station=api.get_current_station(store),
                              brand=None,
                              model=None,
                              type=None,
                              store=store)

    def get_title(self, *args):
        if self.edit_mode:
            return _("Edit Device for %s") % self.model.station.name
        else:
            return _("Add Device")

    def validate_confirm(self):
        if not self.edit_mode:
            settings = DeviceSettings.get_by_station_and_type(
                store=api.get_default_store(),
                station=self.model.station.id,
                type=self.model.type)
            if settings:
                self.station.set_invalid(
                    _(u"A %s already exists for station \"%s\"") %
                    (self.model.get_device_type_name(),
                     self.model.station.name))
                return False
        return True

    #
    # Kiwi callbacks
    #

    def on_brand_combo__changed(self, *args):
        self.update_model_combo()
        self.refresh_ok()

    def on_type_combo__changed(self, *args):
        self.update_brand_combo()
        self.refresh_ok()

    def on_brand_combo__state_changed(self, *args):
        self.update_model_combo()
        self.refresh_ok()

    def on_model_combo__changed(self, *args):
        self.refresh_ok()

    def on_device_combo__changed(self, *args):
        self.refresh_ok()
Exemple #9
0
class DeviceSettingsEditor(BaseEditor):
    gladefile = 'DeviceSettingsEditor'
    model_type = DeviceSettings
    proxy_widgets = ('type_combo', 'brand_combo', 'device_combo',
                     'model_combo', 'baudrate', 'station', 'is_active_button')

    def __init__(self, store, model=None, station=None):
        if station is not None and not isinstance(station, BranchStation):
            raise TypeError("station should be a BranchStation")

        self._device_manager = DeviceManager()
        self._branch_station = station
        # This attribute is set to True when setup_proxies is finished
        self._is_initialized = False
        BaseEditor.__init__(self, store, model)
        self._original_brand = self.model.brand
        self._original_model = self.model.model
        self.test_button = self.add_button(_('Test device'))

    def refresh_ok(self, *args):
        if not self._is_initialized:
            return
        if self.test_button:
            self.test_button.set_sensitive(self.model.is_valid())
        BaseEditor.refresh_ok(self, self.model.is_valid())

    def setup_station_combo(self):
        if self._branch_station:
            self.station.prefill([(self._branch_station.name,
                                   self._branch_station)])
            self.model.station = self._branch_station
            return

        self.station.prefill([
            (station.name, station)
            for station in BranchStation.get_active_stations(self.store)
        ])

    def setup_device_port_combo(self):
        items = [(_("Choose..."), None)]
        items.extend([(str(device.device_name), str(device.device_name))
                      for device in self._device_manager.get_serial_devices()])
        items.extend(self._get_usb_devices())

        if is_developer_mode():
            # Include virtual port for virtual printer
            items.append(('Virtual device', u'/dev/null'))

        devices = [i[1] for i in items]
        if self.model.device_name not in devices:
            items.append(('Unkown device (%s)' % self.model.device_name,
                          self.model.device_name))
        self.device_combo.prefill(items)

    def setup_device_types_combo(self):
        items = [(_("Choose..."), None)]
        device_types = (
            # TODO: Reenable when we have cheque printers working.
            # DeviceSettings.CHEQUE_PRINTER_DEVICE,
            DeviceSettings.NON_FISCAL_PRINTER_DEVICE,
            DeviceSettings.SCALE_DEVICE,
        )

        items.extend([(self.model.describe_device_type(t), t)
                      for t in device_types])
        self.type_combo.prefill(items)

    def setup_widgets(self):
        self.setup_device_types_combo()
        self.setup_device_port_combo()
        self.setup_station_combo()
        self.baudrate.prefill(get_baudrate_values())
        if not self.edit_mode:
            self.is_active_button.set_sensitive(False)

    def _get_supported_types(self):
        if self.model.type == DeviceSettings.SCALE_DEVICE:
            supported_types = get_supported_scales()
        #elif self.model.type == DeviceSettings.CHEQUE_PRINTER_DEVICE:
        #    supported_types = get_supported_printers_by_iface(IChequePrinter)
        elif self.model.type == DeviceSettings.NON_FISCAL_PRINTER_DEVICE:
            supported_types = get_supported_printers_by_iface(
                INonFiscalPrinter, include_virtual=is_developer_mode())
        else:
            raise TypeError("The selected device type isn't supported")
        return supported_types

    def _get_usb_devices(self):
        try:
            import usb.core
        except ImportError:  # pragma no cover
            return []

        devices = []
        for device in get_usb_printer_devices():
            try:
                dev = 'usb:{}:{}'.format(hex(device.idVendor),
                                         hex(device.idProduct))
                try:
                    if device.manufacturer is not None:
                        desc = '{} {}'.format(device.manufacturer,
                                              device.product)
                    else:
                        desc = dev
                except Exception:
                    desc = dev

                devices.append((desc, dev))
            except usb.core.USBError:  # pragma no cover
                # The user might not have access to some devices
                continue

        return devices

    def _get_supported_brands(self):
        return list(self._get_supported_types().keys())

    def _get_supported_models(self):
        return self._get_supported_types()[self.model.brand]

    def update_brand_combo(self):
        self.brand_combo.clear()
        self.brand_combo.set_sensitive(self.model.type is not None)
        if self.model.type is None:
            return
        items = [(_("Choose..."), None)]
        supported_brands = self._get_supported_brands()
        items.extend([(brand.capitalize(), str(brand))
                      for brand in supported_brands])
        self.brand_combo.prefill(items)

    def update_model_combo(self):
        self.model_combo.clear()
        brand = self.model.brand
        self.model_combo.set_sensitive(brand is not None)
        if self.model.brand is None:
            return
        supported_models = self._get_supported_models()
        items = [(_("Choose..."), None)]
        items.extend([(obj.model_name, str(obj.__name__))
                      for obj in supported_models])
        self.model_combo.prefill(items)

    #
    # BaseEditor hooks
    #

    def setup_proxies(self):
        self.setup_widgets()
        self.proxy = self.add_proxy(self.model,
                                    DeviceSettingsEditor.proxy_widgets)
        self._is_initialized = True

    def create_model(self, store):
        return DeviceSettings(device_name=None,
                              station=api.get_current_station(store),
                              baudrate=9600,
                              brand=None,
                              model=None,
                              type=None,
                              store=store)

    def get_title(self, *args):
        if self.edit_mode:
            return _("Edit Device for %s") % self.model.station.name
        else:
            return _("Add Device")

    def validate_confirm(self):
        settings = DeviceSettings.get_by_station_and_type(
            store=api.get_default_store(),
            station=self.model.station.id,
            type=self.model.type,
            exclude=self.model)
        if settings and self.is_active_button.get_active():
            warning(
                _(u"An active %s already exists for station \"%s\"") %
                (self.model.device_type_name, self.model.station_name))
            return False

        return True

    #
    # Kiwi callbacks
    #

    def on_brand_combo__changed(self, *args):
        self.update_model_combo()
        self.refresh_ok()

    def on_type_combo__changed(self, *args):
        self.update_brand_combo()
        self.refresh_ok()

    def on_model_combo__changed(self, *args):
        self.refresh_ok()

    def on_device_combo__changed(self, *args):
        self.refresh_ok()

    def on_test_button__clicked(self, button):
        driver = self.model.get_interface()
        if self.model.type == DeviceSettings.NON_FISCAL_PRINTER_DEVICE:
            with driver.open():
                driver.print_line(TEST_MESSAGE)
        elif self.model.type == DeviceSettings.SCALE_DEVICE:
            data = read_scale_info(self.store, self.model)
            warning(str(data.weight))