Exemplo n.º 1
0
 def fields(self):
     return collections.OrderedDict(
         branch_id=PersonField(_('Branch'),
                               proxy=True,
                               person_type=Branch,
                               can_add=False,
                               can_edit=False,
                               mandatory=True),
         method=PaymentMethodField(_('Method'),
                                   payment_type=self.payment_type,
                                   proxy=True,
                                   mandatory=True,
                                   separate=True),
         account=ChoiceField(self.account_label),
         description=TextField(_('Description'), proxy=True,
                               mandatory=True),
         person_id=PersonField(person_type=self.person_type, proxy=True),
         value=PriceField(_('Value'), proxy=True, mandatory=True),
         due_date=DateField(_('Due date'), proxy=True, mandatory=True),
         category=PaymentCategoryField(_('Category'),
                                       category_type=self.category_type,
                                       proxy=True),
         repeat=ChoiceField(_('Repeat')),
         end_date=DateField(_('End date')),
         attachment=AttachmentField(_('Attachment')))
Exemplo n.º 2
0
 def fields(self):
     return collections.OrderedDict(
         expire_at=DateField(_('Expire at'), proxy=True),
         branch_id=PersonField(_('Branch'), proxy=True, person_type=Branch),
         profile_id=UserProfileField(_('User profile'), proxy=True),
         user_id=PersonField(_('User'), proxy=True, person_type=LoginUser),
         content=MultiLineField(_('Message'), proxy=True),
     )
Exemplo n.º 3
0
    def test_run_dialog(self):
        client = self.create_client()

        field = PersonField(person_type=Client)
        field.form = None

        with mock.patch('stoqlib.gui.wizards.personwizard.run_dialog') as run_dialog:
            field.run_dialog(self.store, client)
            run_dialog.assert_called_once_with(ClientEditor, None, self.store, client,
                                               visual_mode=False)
Exemplo n.º 4
0
    def test_run_dialog(self):
        client = self.create_client()

        field = PersonField(person_type=Client)
        field.form = None

        with mock.patch(
                'stoqlib.gui.wizards.personwizard.run_dialog') as run_dialog:
            field.run_dialog(self.store, client)
            run_dialog.assert_called_once_with(ClientEditor,
                                               None,
                                               self.store,
                                               client,
                                               visual_mode=False)
Exemplo n.º 5
0
    def fields(self):
        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))
        freight_types = [(v, k) for k, v in Delivery.freights.items()]

        return collections.OrderedDict(
            client_str=TextField(_("Client"), proxy=True, editable=False),
            transporter_id=PersonField(_("Transporter"),
                                       proxy=True,
                                       person_type=Transporter,
                                       can_add=can_modify_transporter,
                                       can_edit=can_modify_transporter),
            address=AddressField(_("Address"), proxy=True, mandatory=True),
            is_sent_check=BoolField(_("Was sent to deliver?")),
            send_date=DateField(_("Send date"), mandatory=True, proxy=True),
            tracking_code=TextField(_("Tracking code"), proxy=True),
            freight_type=ChoiceField(_("Freight type"),
                                     proxy=True,
                                     values=freight_types),
            volumes_kind=TextField(_("Volumes kind"), proxy=True),
            volumes_quantity=IntegerField(_("Volumes quantity"), proxy=True),
            is_received_check=BoolField(_("Was received by client?")),
            receive_date=DateField(_("Receive date"),
                                   mandatory=True,
                                   proxy=True),
            empty=EmptyField(),
        )
Exemplo n.º 6
0
    def fields(self):
        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))
        freight_types = [(v, k) for k, v in Delivery.freights.items()]

        return collections.OrderedDict(
            client=PersonQueryField(_("Client"),
                                    proxy=True,
                                    mandatory=True,
                                    person_type=Client),
            transporter_id=PersonField(_("Transporter"),
                                       proxy=True,
                                       person_type=Transporter,
                                       can_add=can_modify_transporter,
                                       can_edit=can_modify_transporter),
            address=AddressField(_("Address"), proxy=True, mandatory=True),
            freight_type=ChoiceField(_("Freight type"),
                                     proxy=True,
                                     values=freight_types),
            price=PriceField(_("Delivery cost"), proxy=True),
            estimated_fix_date=DateField(_("Estimated delivery date"),
                                         proxy=True),
            volumes_kind=TextField(_("Volumes kind"), proxy=True),
            volumes_quantity=IntegerField(_("Volumes quantity"), proxy=True),
        )
Exemplo n.º 7
0
    def fields(self):
        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))
        freight_types = [(v, k) for k, v in Delivery.freights.items()]
        states = [(v, v) for v in api.get_l10n_field('state').state_list]

        return collections.OrderedDict(
            recipient=PersonQueryField(_("Recipient"), proxy=True, mandatory=True,
                                       person_type=self.person_type),
            transporter_id=PersonField(_("Transporter"), proxy=True,
                                       person_type=Transporter,
                                       can_add=can_modify_transporter,
                                       can_edit=can_modify_transporter),
            address=AddressField(_("Address"), proxy=True, mandatory=True),
            freight_type=ChoiceField(_("Freight type"), proxy=True,
                                     values=freight_types),
            price=PriceField(_("Delivery cost"), proxy=True),
            estimated_fix_date=DateField(_("Estimated delivery date"), proxy=True),
            volumes_kind=TextField(_("Volumes kind"), proxy=True),
            volumes_quantity=IntegerField(_("Volumes quantity"), proxy=True),
            volumes_net_weight=NumericField(_("Volumes net weight"), proxy=True,
                                            digits=3),
            volumes_gross_weight=NumericField(_("Volumes gross weight"),
                                              proxy=True, digits=3),
            vehicle_license_plate=TextField(_("Vehicle license plate"), proxy=True),
            vehicle_state=ChoiceField(_("Vehicle state"), proxy=True, use_entry=True,
                                      values=states),
            vehicle_registration=TextField(_("Vehicle registration"), proxy=True),
        )
Exemplo n.º 8
0
    def fields(self):
        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))

        return collections.OrderedDict(
            client_str=TextField(_("Client"),
                                 proxy=True,
                                 editable=False,
                                 colspan=2),
            transporter_id=PersonField(_("Transporter"),
                                       proxy=True,
                                       person_type=Transporter,
                                       colspan=2,
                                       can_add=can_modify_transporter,
                                       can_edit=can_modify_transporter),
            address=AddressField(_("Address"),
                                 proxy=True,
                                 mandatory=True,
                                 colspan=2),
            was_delivered_check=BoolField(_("Was sent to deliver?")),
            deliver_date=DateField(_("Delivery date"),
                                   mandatory=True,
                                   proxy=True),
            tracking_code=TextField(_("Tracking code"), proxy=True),
            was_received_check=BoolField(_("Was received by client?")),
            receive_date=DateField(_("Receive date"),
                                   mandatory=True,
                                   proxy=True),
            empty=EmptyField(),
        )
Exemplo n.º 9
0
    def fields(self):
        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))

        return collections.OrderedDict(
            client_id=PersonField(_("Client"), proxy=True, mandatory=True,
                                  person_type=Client),
            transporter_id=PersonField(_("Transporter"), proxy=True,
                                       person_type=Transporter,
                                       can_add=can_modify_transporter,
                                       can_edit=can_modify_transporter),
            address=AddressField(_("Address"), proxy=True, mandatory=True),
            price=PriceField(_("Delivery cost"), proxy=True),
            estimated_fix_date=DateField(_("Estimated delivery date"), proxy=True),
        )
Exemplo n.º 10
0
 def fields(self):
     return collections.OrderedDict(
         name=TextField(_('Name'), proxy=True, mandatory=True),
         code=TextField(_('Code'), proxy=True, mandatory=True),
         branch_id=PersonField(_('Branch'),
                               proxy=True,
                               person_type=Branch,
                               can_add=False,
                               can_edit=False,
                               mandatory=True),
     )
Exemplo n.º 11
0
class CreditCheckHistoryEditor(BaseEditor):
    model_type = CreditCheckHistory
    model_name = _("Client Credit Check History")
    size = (400, -1)

    fields = dict(
        client=PersonField(_('Client'), proxy=True, person_type=Client,
                           mandatory=True),
        identifier=TextField(_('Identifier'), proxy=True, mandatory=True),
        status=ChoiceField('Status', mandatory=True),
        check_date=DateField(_('Date'), proxy=True),
        user=ChoiceField(_('User')),
        notes=MultiLineField(_('Notes'), proxy=True),
    )

    def __init__(self, store, model, client, visual_mode=None):
        self._client = client
        self.fields['status'].values = self.get_status_options()

        BaseEditor.__init__(self, store, model, visual_mode)

        if visual_mode or client:
            self.client_add_button.hide()
            self.client_edit_button.hide()

        if self.model.client:
            self.set_description(_('client credit check history for %s') %
                                 self.model.client.person.name)
            self.client.set_sensitive(False)
        else:
            self.set_description(_('client credit check history'))

    def create_model(self, store):
        return CreditCheckHistory(check_date=localtoday().date(),
                                  identifier=u'',
                                  status=CreditCheckHistory.STATUS_NOT_INCLUDED,
                                  client=self._client,
                                  notes=u'',
                                  user=api.get_current_user(self.store),
                                  store=store)

    def setup_proxies(self):
        self._fill_user_field()

    def _fill_user_field(self):
        self.user.prefill([(self.model.user.person.name,
                            self.model.user)])
        self.user.set_sensitive(False)

    @classmethod
    def get_status_options(cls):
        return [(value, key) for key, value in CreditCheckHistory.statuses.items()]
Exemplo n.º 12
0
 def fields(self):
     return collections.OrderedDict(
         client_id=PersonField(_('Client'),
                               proxy=True,
                               person_type=Client,
                               mandatory=True),
         identifier=TextField(_('Identifier'), proxy=True, mandatory=True),
         status=ChoiceField('Status',
                            values=self.get_status_options(),
                            mandatory=True),
         check_date=DateField(_('Date'), proxy=True),
         user=ChoiceField(_('User')),
         notes=MultiLineField(_('Notes'), proxy=True),
     )
Exemplo n.º 13
0
 def fields(self):
     return collections.OrderedDict(
         identifier=TextField(_("Sale #"), proxy=True, editable=False),
         open_date=DateTextField(_("Open date"), proxy=True,
                                 editable=False),
         status_str=TextField(_("Status"), proxy=True, editable=False),
         salesperson_id=PersonField(_("Salesperson"),
                                    proxy=True,
                                    can_add=False,
                                    can_edit=False,
                                    person_type=SalesPerson),
         client=PersonQueryField(_("Client"),
                                 proxy=True,
                                 person_type=Client),
     )
Exemplo n.º 14
0
    def fields(self):
        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))
        freight_types = [(v, k) for k, v in Delivery.freights.items()]
        states = [(v, v) for v in api.get_l10n_field('state').state_list]

        return collections.OrderedDict(
            recipient_str=TextField(_("Recipient"), proxy=True,
                                    editable=False),
            transporter_id=PersonField(_("Transporter"),
                                       proxy=True,
                                       person_type=Transporter,
                                       can_add=can_modify_transporter,
                                       can_edit=can_modify_transporter),
            address=AddressField(_("Address"), proxy=True, mandatory=True),
            is_sent_check=BoolField(_("Was sent to deliver?")),
            send_date=DateField(_("Send date"), mandatory=True, proxy=True),
            tracking_code=TextField(_("Tracking code"), proxy=True),
            freight_type=ChoiceField(_("Freight type"),
                                     proxy=True,
                                     values=freight_types),
            volumes_kind=TextField(_("Volumes kind"), proxy=True),
            volumes_quantity=IntegerField(_("Volumes quantity"), proxy=True),
            volumes_net_weight=NumericField(_("Volumes net weight"),
                                            proxy=True,
                                            digits=3),
            volumes_gross_weight=NumericField(_("Volumes gross weight"),
                                              proxy=True,
                                              digits=3),
            vehicle_license_plate=TextField(_("Vehicle license plate"),
                                            proxy=True),
            vehicle_state=ChoiceField(_("Vehicle state"),
                                      proxy=True,
                                      use_entry=True,
                                      values=states),
            vehicle_registration=TextField(_("Vehicle registration"),
                                           proxy=True),
            is_received_check=BoolField(_("Was received by recipient?")),
            receive_date=DateField(_("Receive date"),
                                   mandatory=True,
                                   proxy=True),
            empty=EmptyField(),
        )
Exemplo n.º 15
0
class DeliveryEditor(BaseEditor):
    """An editor for :class:`stoqlib.domain.sale.Delivery`"""

    title = _("Delivery editor")
    gladefile = 'DeliveryEditor'
    size = (600, 400)
    model_type = Delivery
    model_name = _('Delivery')
    form_holder_name = 'forms'

    proxy_widgets = [
        'client_str',
        'deliver_date',
        'receive_date',
        'tracking_code',
    ]

    fields = dict(
        transporter=PersonField(_("Transporter"), proxy=True,
                                person_type=Transporter),
        address=AddressField(_("Address"), proxy=True, mandatory=True)
    )

    def __init__(self, store, *args, **kwargs):
        self._configuring_proxies = False

        super(DeliveryEditor, self).__init__(store, *args, **kwargs)

    #
    #  BaseEditor Hooks
    #

    def setup_proxies(self):
        self._configuring_proxies = True
        self._setup_widgets()
        self.proxy = self.add_proxy(self.model, DeliveryEditor.proxy_widgets)
        self._update_status_widgets()
        self._configuring_proxies = False

    def setup_slaves(self):
        self.delivery_items = ObjectList(
            columns=self._get_delivery_items_columns(),
            objects=self.model.delivery_items,
        )
        self.delivery_items_holder.add(self.delivery_items)
        self.delivery_items.show()

    #
    #  Private
    #

    def _setup_widgets(self):
        for widget in (self.receive_date, self.deliver_date,
                       self.tracking_code):
            widget.set_sensitive(False)

        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))
        self.fields['transporter'].can_add = can_modify_transporter
        self.fields['transporter'].can_edit = can_modify_transporter

    def _update_status_widgets(self):
        if self.model.status == Delivery.STATUS_INITIAL:
            for widget in (self.was_delivered_check, self.was_received_check):
                widget.set_active(False)
        elif self.model.status == Delivery.STATUS_SENT:
            self.was_delivered_check.set_active(True)
            self.was_received_check.set_active(False)
        elif self.model.status == Delivery.STATUS_RECEIVED:
            for widget in (self.was_delivered_check, self.was_received_check):
                widget.set_active(True)
        else:
            raise ValueError(_("Invalid status for %s") % (
                             self.model.__class__.__name__))

    def _get_delivery_items_columns(self):
        return [
            Column('sellable.description', title=_('Products to deliver'),
                   data_type=str, expand=True, sorted=True),
            Column('quantity', title=_('Quantity'), data_type=decimal.Decimal,
                   format_func=format_quantity),
        ]

    #
    #  Callbacks
    #

    def on_was_delivered_check__toggled(self, button):
        active = button.get_active()
        # When delivered, don't let user change transporter or address
        self.transporter.set_sensitive(not active)
        self.address.set_sensitive(not active)
        for widget in (self.deliver_date, self.tracking_code):
            widget.set_sensitive(active)

        if not self.model.deliver_date:
            self.proxy.update('deliver_date', localtoday().date())

        if self._configuring_proxies:
            # Do not change status above
            return

        if active:
            self.model.set_sent()
        else:
            self.model.set_initial()

    def on_was_received_check__toggled(self, button):
        active = button.get_active()
        self.receive_date.set_sensitive(active)
        # If it was received, don't let the user unmark was_delivered_check
        self.was_delivered_check.set_sensitive(not active)

        if not self.was_delivered_check.get_active():
            self.was_delivered_check.set_active(True)

        if not self.model.receive_date:
            self.proxy.update('receive_date', localtoday().date())

        if self._configuring_proxies:
            # Do not change status above
            return

        if active:
            self.model.set_received()
        else:
            self.model.set_sent()
Exemplo n.º 16
0
class _PaymentEditor(BaseEditor):
    confirm_widgets = ['due_date']
    model_type = Payment
    model_name = _('payment')

    # Override in subclass
    person_type = None
    category_type = None
    payment_type = None

    fields = dict(branch=PersonField(_('Branch'),
                                     proxy=True,
                                     person_type=Branch,
                                     can_add=False,
                                     can_edit=False,
                                     mandatory=True),
                  method=PaymentMethodField(_('Method'),
                                            proxy=True,
                                            mandatory=True,
                                            separate=True),
                  description=TextField(_('Description'),
                                        proxy=True,
                                        mandatory=True),
                  person=PersonField(proxy=True),
                  value=PriceField(_('Value'), proxy=True, mandatory=True),
                  due_date=DateField(_('Due date'), proxy=True,
                                     mandatory=True),
                  category=PaymentCategoryField(_('Category'), proxy=True),
                  repeat=ChoiceField(_('Repeat')),
                  end_date=DateField(_('End date')),
                  attachment=AttachmentField(_('Attachment')))

    def __init__(self, store, model=None, category=None):
        """ A base class for additional payments

        :param store: a store
        :param model: a :class:`stoqlib.domain.payment.payment.Payment` object or None

        """
        self.fields['person'].person_type = self.person_type
        self.fields['category'].category_type = self.category_type
        self.fields['method'].payment_type = self.payment_type

        BaseEditor.__init__(self, store, model)
        self._setup_widgets()
        if category:
            self.category.select_item_by_label(category)
        self.description.grab_focus()

    #
    # BaseEditor hooks
    #

    def create_model(self, store):
        group = PaymentGroup()
        money = PaymentMethod.get_by_name(store, u'money')
        branch = api.get_current_branch(store)
        # Set status to PENDING now, to avoid calling set_pending on
        # on_confirm for payments that shoud not have its status changed.
        return Payment(open_date=localtoday().date(),
                       branch=branch,
                       status=Payment.STATUS_PENDING,
                       description=u'',
                       value=currency(0),
                       base_value=currency(0),
                       due_date=None,
                       method=money,
                       group=group,
                       till=None,
                       category=None,
                       payment_type=self.payment_type,
                       bill_received=False)

    def setup_proxies(self):
        repeat_types = get_interval_type_items(with_multiples=True,
                                               adverb=True)
        repeat_types.insert(0, (_('Once'), _ONCE))
        self.repeat.prefill(repeat_types)
        self.add_proxy(self.model, _PaymentEditor.proxy_widgets)

    def validate_confirm(self):
        if (self.repeat.get_selected() != _ONCE and not self._validate_date()):
            return False
        # FIXME: the kiwi view should export it's state and it should
        #        be used by default
        return bool(self.model.description and self.model.due_date
                    and self.model.value)

    def on_confirm(self):
        self.model.base_value = self.model.value
        person = self.person.get_selected_data()
        if (person is not None and person is not ValueUnset and
                # FIXME: PersonField should never let get_selected_data()
                #        return anything different from None and the model.
                person != ""):
            setattr(self.model.group, self.person_attribute,
                    self.store.fetch(person.person))

        self.model.attachment = self.fields['attachment'].attachment

        self.store.add(self.model.group)
        self.store.add(self.model)

        if self.repeat.get_selected() != _ONCE:
            Payment.create_repeated(self.store, self.model,
                                    self.repeat.get_selected(),
                                    self.model.due_date.date(),
                                    self.end_date.get_date())

    # Private

    def _setup_widgets(self):
        self.person_lbl.set_label(self._person_label)
        if self.model.group.sale:
            label = _("Sale details")
        elif self.model.group.purchase:
            label = _("Purchase details")
        elif self.model.group._renegotiation:
            label = _("Details")
        else:
            label = _("Details")
        self.details_button = self.add_button(label)
        self.details_button.connect('clicked',
                                    self._on_details_button__clicked)

        self.end_date.set_sensitive(False)
        if self.edit_mode:
            for field_name in [
                    'value', 'due_date', 'person', 'repeat', 'end_date',
                    'branch', 'method'
            ]:
                field = self.fields[field_name]
                field.can_add = False
                field.can_edit = False
                field.set_sensitive(False)

        person = getattr(self.model.group, self.person_attribute)
        if person:
            store = person.store
            facet = store.find(self.person_type, person=person).one()
            self.person.select(facet)

    def _show_order_dialog(self):
        group = self.model.group
        if group.sale:
            sale_view = self.store.find(SaleView, id=group.sale.id).one()
            run_dialog(SaleDetailsDialog, self, self.store, sale_view)
        elif group.purchase:
            run_dialog(PurchaseDetailsDialog, self, self.store, group.purchase)
        elif group._renegotiation:
            run_dialog(RenegotiationDetailsDialog, self, self.store,
                       group._renegotiation)
        elif group.stock_decrease:
            run_dialog(StockDecreaseDetailsDialog, self, self.store,
                       group.stock_decrease)
        else:
            run_dialog(LonelyPaymentDetailsDialog, self, self.store,
                       self.model)

    def _get_min_date_for_interval(self, due_date, interval_type):
        if not due_date or interval_type is None:
            return None
        return due_date + interval_type_as_relativedelta(interval_type)

    def _validate_date(self):
        if not self.end_date.props.sensitive:
            return True
        end_date = self.end_date.get_date()
        due_date = self.due_date.get_date()
        min_date = self._get_min_date_for_interval(due_date,
                                                   self.repeat.read())

        if end_date and due_date:
            if end_date < due_date:
                self.end_date.set_invalid(
                    _("End date cannot be before start date"))
            elif min_date and end_date < min_date:
                self.end_date.set_invalid(
                    _("End date must be after %s for this "
                      "repeat interval") % min_date.strftime('%x'))
            else:
                self.end_date.set_valid()
                self.refresh_ok(self.is_valid)
                return True
        elif not end_date:
            self.end_date.set_invalid(_("Date cannot be empty"))
        elif not due_date:
            self.due_date.set_invalid(_("Date cannot be empty"))
        self.refresh_ok(False)
        return False

    def _validate_person(self):
        payment_type = self.payment_type
        method = self.method.get_selected()
        if method.operation.require_person(payment_type):
            self.person.set_property('mandatory', True)
        else:
            self.person.set_property('mandatory', False)

    #
    # Kiwi Callbacks
    #

    def on_value__validate(self, widget, newvalue):
        if newvalue is None or newvalue <= 0:
            return ValidationError(_("The value must be greater than zero."))

    def on_repeat__content_changed(self, repeat):
        if repeat.get_selected() == _ONCE:
            self.end_date.set_sensitive(False)

            # FIXME: need this check so tests won't crash
            if hasattr(self, 'main_dialog'):
                self.refresh_ok(True)

            return

        self.end_date.set_sensitive(True)
        self._validate_date()

    def on_due_date__content_changed(self, due_date):
        self._validate_date()

    def on_end_date__content_changed(self, end_date):
        self._validate_date()

    def _on_details_button__clicked(self, widget):
        self._show_order_dialog()

    def on_method__content_changed(self, method):
        self.person.validate(force=True)
        self._validate_person()
Exemplo n.º 17
0
class CreateDeliveryEditor(BaseEditor):
    """A fake delivery editor implementation.

    This is used to get get information for creating a delivery,
    without really creating a it.
    """

    model_name = _('Delivery')
    model_type = _CreateDeliveryModel
    form_holder_name = 'forms'
    gladefile = 'CreateDeliveryEditor'
    title = _('New Delivery')
    size = (750, 550)

    proxy_widgets = [
        'estimated_fix_date',
        'price',
    ]

    fields = dict(
        client=PersonField(_("Client"), proxy=True, mandatory=True,
                           person_type=Client),
        transporter=PersonField(_("Transporter"), proxy=True,
                                person_type=Transporter),
        address=AddressField(_("Address"), proxy=True, mandatory=True),
    )

    def __init__(self, store, model=None, sale_items=None):
        self.sale_items = sale_items
        self._deliver_items = []

        if not model:
            for sale_item in sale_items:
                sale_item.deliver = True
        else:
            model.create_savepoint()
            # Store this information for later rollback.
            for sale_item in sale_items:
                self._deliver_items.append(sale_item.deliver)

        BaseEditor.__init__(self, store, model)
        self._setup_widgets()

    def _setup_widgets(self):
        self.additional_info_label.set_size('small')
        self.additional_info_label.set_color('red')
        self.register_validate_function(self._validate_widgets)
        self.set_description(self.model_name)

        # Only users with admin or purchase permission can modify transporters
        user = api.get_current_user(self.store)
        can_modify_transporter = any((
            user.profile.check_app_permission(u'admin'),
            user.profile.check_app_permission(u'purchase'),
        ))
        self.fields['transporter'].can_add = can_modify_transporter
        self.fields['transporter'].can_edit = can_modify_transporter

        self.fields['client'].person_type = Client

        self._update_widgets()

    def _validate_widgets(self, validation_value):
        if validation_value:
            validation_value = any(item.deliver for item in self.items)
        self.refresh_ok(validation_value)

    def _update_widgets(self):
        if self.model.notes:
            self.additional_info_label.show()
        else:
            self.additional_info_label.hide()

    def _get_sale_items_columns(self):
        return [Column('code', title=_('Code'),
                       data_type=str),
                Column('description', title=_('Description'),
                       data_type=str, expand=True),
                Column('quantity', title=_('Quantity'),
                       data_type=decimal.Decimal, format_func=format_quantity),
                Column('deliver', title=_('Deliver'),
                       data_type=bool, editable=True)]

    #
    # Callbacks
    #

    def on_additional_info_button__clicked(self, button):
        if run_dialog(NoteEditor, self, self.store, self.model, 'notes',
                      title=_('Delivery Instructions')):
            self._update_widgets()

    def on_estimated_fix_date__validate(self, widget, date):
        if date < localtoday().date():
            return ValidationError(_("Expected delivery date must "
                                     "be set to a future date"))

    def on_price__validate(self, widget, price):
        if price < 0:
            return ValidationError(
                _("The Delivery cost must be a positive value."))

    def on_client__content_changed(self, combo):
        client = combo.get_selected_data()
        if client:
            self.fields['address'].set_from_client(client)

    def _on_items__cell_edited(self, items, item, attribute):
        self.force_validation()

    #
    # BaseEditor hooks
    #

    def create_model(self, store):
        price = sysparam(self.store).DELIVERY_SERVICE.sellable.price
        return _CreateDeliveryModel(price=price)

    def setup_proxies(self):
        self.proxy = self.add_proxy(self.model,
                                    CreateDeliveryEditor.proxy_widgets)

    def setup_slaves(self):
        self.items = ObjectList(columns=self._get_sale_items_columns(),
                                objects=self.sale_items)
        self.items.connect('cell-edited', self._on_items__cell_edited)
        self.addition_list_holder.add(self.items)
        self.items.show()

    def on_cancel(self):
        # FIXME: When Kiwi allows choosing proxies to save upon confirm, apply
        # that here instead of making this rollback by hand. Bug 5415.
        self.model.rollback_to_savepoint()
        if self._deliver_items:
            for sale_item, deliver in zip(self.sale_items, self._deliver_items):
                sale_item.deliver = deliver

    def on_confirm(self):
        estimated_fix_date = self.estimated_fix_date.read()
        for sale_item in self.sale_items:
            sale_item.estimated_fix_date = estimated_fix_date