def test_can_purchase_allow_all(self): #: This parameter always allows the client to purchase, no matter if he #: has late payments sysparam(self.store).update_parameter(u'LATE_PAYMENTS_POLICY', unicode(int(LatePaymentPolicy.ALLOW_SALES))) client = self.create_client() bill_method = PaymentMethod.get_by_name(self.store, u'bill') check_method = PaymentMethod.get_by_name(self.store, u'check') money_method = PaymentMethod.get_by_name(self.store, u'money') store_credit_method = PaymentMethod.get_by_name(self.store, u'store_credit') today = localtoday() # client can pay if he doesn't have any payments client.credit_limit = Decimal("1000") self.assertTrue(client.can_purchase(money_method, currency("200"))) # client can pay if he has payments that are not overdue payment = self.create_payment(Payment.TYPE_IN, today, method=bill_method) payment.group = self.create_payment_group() payment.group.payer = client.person self.assertTrue(client.can_purchase(check_method, currency("200"))) # client can pay even if he does have overdue payments payment = self.create_payment(Payment.TYPE_IN, today - relativedelta(days=1), method=check_method) payment.group = self.create_payment_group() payment.group.payer = client.person self.assertTrue(client.can_purchase(store_credit_method, currency("200"))) # But he cannot pay if its above the credit limit self.assertRaises(SellError, client.can_purchase, store_credit_method, currency("1001"))
def test_get_payment_by_method_name(self): group = self.create_payment_group() method = PaymentMethod.get_by_name(self.store, u'money') money_payment1 = self.create_payment(method=method) group.add_item(money_payment1) money_payment2 = self.create_payment(method=method) group.add_item(money_payment2) method = PaymentMethod.get_by_name(self.store, u'check') check_payment1 = self.create_payment(method=method) group.add_item(check_payment1) check_payment2 = self.create_payment(method=method) group.add_item(check_payment2) money_payments = group.get_payments_by_method_name(u'money') for payment in [money_payment1, money_payment2]: self.assertTrue(payment in money_payments) for payment in [check_payment1, check_payment2]: self.assertFalse(payment in money_payments) check_payments = group.get_payments_by_method_name(u'check') for payment in [check_payment1, check_payment2]: self.assertTrue(payment in check_payments) for payment in [money_payment1, money_payment2]: self.assertFalse(payment in check_payments)
def test_can_purchase_disallow_store_credit(self): #: This parameter disallows the client to purchase with store credit #: when he has late payments sysparam(self.store).update_parameter(u'LATE_PAYMENTS_POLICY', unicode(int(LatePaymentPolicy.DISALLOW_STORE_CREDIT))) client = self.create_client() bill_method = PaymentMethod.get_by_name(self.store, u'bill') check_method = PaymentMethod.get_by_name(self.store, u'check') money_method = PaymentMethod.get_by_name(self.store, u'money') store_credit_method = PaymentMethod.get_by_name(self.store, u'store_credit') today = localtoday() # client can pay if he doesn't have any payments self.assertTrue(client.can_purchase(money_method, currency("0"))) # client can pay if he has payments that are not overdue payment = self.create_payment(Payment.TYPE_IN, today, method=bill_method) payment.group = self.create_payment_group() payment.group.payer = client.person self.assertTrue(client.can_purchase(money_method, currency("0"))) # for a client with overdue payments payment = self.create_payment(Payment.TYPE_IN, today - relativedelta(days=1), method=money_method) payment.status = Payment.STATUS_PENDING payment.group = self.create_payment_group() payment.group.payer = client.person # client can pay if payment method is not store credit self.assertTrue(client.can_purchase(check_method, currency("0"))) self.assertTrue(client.can_purchase(money_method, currency("0"))) # client can not pay if payment method is store credit self.assertRaises(SellError, client.can_purchase, store_credit_method, currency("0"))
def test_pay_money_payments(self): branch = self.create_branch() group = self.create_payment_group() method = PaymentMethod.get_by_name(self.store, u'bill') payment1 = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(10)) payment2 = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(10)) method = PaymentMethod.get_by_name(self.store, u'money') method.max_installments = 2 payment3 = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(10)) payment4 = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(10)) group.confirm() self.assertEqual(payment1.status, Payment.STATUS_PENDING) self.assertEqual(payment2.status, Payment.STATUS_PENDING) self.assertEqual(payment3.status, Payment.STATUS_PENDING) self.assertEqual(payment4.status, Payment.STATUS_PENDING) payment3.pay() self.assertEqual(payment3.status, Payment.STATUS_PAID) group.pay_method_payments(u'money') self.assertEqual(payment1.status, Payment.STATUS_PENDING) self.assertEqual(payment2.status, Payment.STATUS_PENDING) self.assertEqual(payment3.status, Payment.STATUS_PAID) self.assertEqual(payment4.status, Payment.STATUS_PAID)
def test_order_receive_sell(self): product = self.create_product() storable = Storable(product=product, store=self.store) self.failIf(self.store.find(ProductStockItem, storable=storable).one()) purchase_order = self.create_purchase_order() purchase_item = purchase_order.add_item(product.sellable, 1) purchase_order.status = purchase_order.ORDER_PENDING method = PaymentMethod.get_by_name(self.store, u'money') method.create_payment(Payment.TYPE_OUT, purchase_order.group, purchase_order.branch, purchase_order.get_purchase_total()) purchase_order.confirm() receiving_order = self.create_receiving_order(purchase_order) receiving_order.branch = get_current_branch(self.store) self.create_receiving_order_item( receiving_order=receiving_order, sellable=product.sellable, purchase_item=purchase_item, quantity=1) self.failIf(self.store.find(ProductStockItem, storable=storable).one()) receiving_order.confirm() product_stock_item = self.store.find(ProductStockItem, storable=storable).one() self.failUnless(product_stock_item) self.assertEquals(product_stock_item.quantity, 1) sale = self.create_sale() sale.add_sellable(product.sellable) sale.order() method = PaymentMethod.get_by_name(self.store, u'check') method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(100)) sale.confirm() self.assertEquals(product_stock_item.quantity, 0)
def test_create_payment(self): acc = self.create_account() branch = self.create_branch() method = PaymentMethod(method_name=u'Test', destination_account=acc) group = self.create_payment_group() self.create_payment(payment_type=Payment.TYPE_IN, date=None, value=100, method=method, branch=branch, group=group) with self.assertRaisesRegex( PaymentMethodError, ('You can not create more inpayments for this payment ' 'group since the maximum allowed for this payment ' 'method is 1')): method.create_payment(payment_type=Payment.TYPE_IN, payment_group=group, branch=branch, value=100, due_date=None, description=None, base_value=None, payment_number=None) self.create_payment(payment_type=Payment.TYPE_IN, date=None, value=100, method=method, branch=branch, group=group) with self.assertRaises(DatabaseInconsistency): method.create_payment(payment_type=Payment.TYPE_IN, payment_group=group, branch=branch, value=100, due_date=None, description=None, base_value=None, payment_number=None)
def _setup_widgets(self): self.remove_button.hide() if isinstance(self.model, (PaymentRenegotiation, Sale, ReturnedSale, StockDecrease)): payment_type = Payment.TYPE_IN elif isinstance(self.model, PurchaseOrder): payment_type = Payment.TYPE_OUT else: raise AssertionError money_method = PaymentMethod.get_by_name(self.store, u'money') self._add_method(money_method) for method in PaymentMethod.get_creatable_methods( self.store, payment_type, separate=False): if method.method_name in [u'multiple', u'money']: continue self._add_method(method) self.payments.set_columns(self._get_columns()) self.payments.add_list(self.model.group.payments) self.total_value.set_bold(True) self.received_value.set_bold(True) self.missing_value.set_bold(True) self.total_value.update(self._total_value) self.remove_button.set_sensitive(False) self._update_values()
def test_inactivate(self): acc = self.create_account() method = PaymentMethod(method_name=u'Test', destination_account=acc) self.assertIsNone(method.inactivate()) method.is_active = False with self.assertRaises(AssertionError) as error: method.inactivate() self.assertEqual(str(error.exception), 'This provider is already inactive')
def test_till_daily_movement(self): date = datetime.date(2013, 1, 1) # create sale payment sale = self.create_sale() sellable = self.create_sellable() sale.add_sellable(sellable, price=100) sale.identifier = 1000 sale.order() method = PaymentMethod.get_by_name(self.store, u'money') till = Till.get_last_opened(self.store) payment = method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, sale.get_sale_subtotal(), till=till) sale.confirm() sale.group.pay() sale.confirm_date = date payment.identifier = 1010 payment.paid_date = date # create lonely input payment payer = self.create_client() address = self.create_address() address.person = payer.person method = PaymentMethod.get_by_name(self.store, u'money') group = self.create_payment_group() branch = self.create_branch() payment_lonely_input = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(100)) payment_lonely_input.description = u"Test receivable account" payment_lonely_input.group.payer = payer.person payment_lonely_input.set_pending() payment_lonely_input.pay() payment_lonely_input.identifier = 1001 payment_lonely_input.paid_date = date # create purchase payment drawee = self.create_supplier() address = self.create_address() address.person = drawee.person method = PaymentMethod.get_by_name(self.store, u'money') group = self.create_payment_group() branch = self.create_branch() payment = method.create_payment(Payment.TYPE_OUT, group, branch, Decimal(100)) payment.description = u"Test payable account" payment.group.recipient = drawee.person payment.set_pending() payment.pay() payment.identifier = 1002 payment.paid_date = date # create lonely output payment self._diff_expected(TillDailyMovementReport, 'till-daily-movement-report', self.store, date)
def testGetByAccount(self): account = self.create_account() methods = PaymentMethod.get_by_account(self.store, account) self.assertTrue(methods.is_empty()) PaymentMethod(store=self.store, method_name=u'test', destination_account=account) methods = PaymentMethod.get_by_account(self.store, account) self.assertFalse(methods.is_empty())
def test_create_payments_without_installments(self): acc = self.create_account() branch = self.create_branch() method = PaymentMethod(method_name=u'Test', destination_account=acc) group = self.create_payment_group() with self.assertRaises(ValueError) as error: method.create_payments(payment_type=Payment.TYPE_IN, group=group, branch=branch, value=Decimal(100), due_dates=[]) self.assertEqual(str(error.exception), _('Need at least one installment'))
def test_negative_credit(self): method = PaymentMethod.get_by_name(self.store, u'credit') client = self.create_client() group = self.create_payment_group(payer=client.person) payment = self.create_payment(method=method, payment_type=Payment.TYPE_OUT, value=6, group=group) payment.set_pending() payment.pay() editor = CreditEditor(self.store, client) editor.description.set_text('Desc') editor.value.set_text('-5') self.assertValid(editor, ['value']) self.assertSensitive(editor.main_dialog, ['ok_button']) editor.value.set_text('-6') self.assertValid(editor, ['value']) self.assertSensitive(editor.main_dialog, ['ok_button']) editor.value.set_text('-7') self.assertInvalid(editor, ['value']) self.assertNotSensitive(editor.main_dialog, ['ok_button'])
def _create_inpayment(self): sale = self.create_sale() sellable = self.create_sellable() sale.add_sellable(sellable, price=10) method = PaymentMethod.get_by_name(self.store, u'bill') payment = method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(10)) return payment
def trade(self): """Do a trade for this return Almost the same as :meth:`.return_`, but unlike it, this won't generate reversed payments to the client. Instead, it'll generate an inpayment using :obj:`.returned_total` value, so it can be used as an "already paid quantity" on :obj:`.new_sale`. """ assert self.new_sale if self.sale: assert self.sale.can_return() self._clean_not_used_items() store = self.store group = self.group method = PaymentMethod.get_by_name(store, u'trade') description = _(u'Traded items for sale %s') % ( self.new_sale.identifier, ) value = self.returned_total payment = method.create_payment(Payment.TYPE_IN, group, self.branch, value, description=description) payment.set_pending() payment.pay() self._return_sale(payment)
def testSalesPersonReport(self): sysparam(self.store).SALE_PAY_COMMISSION_WHEN_CONFIRMED = 1 salesperson = self.create_sales_person() product = self.create_product(price=100) sellable = product.sellable sale = self.create_sale() sale.salesperson = salesperson sale.add_sellable(sellable, quantity=1) self.create_storable(product, get_current_branch(self.store), stock=100) CommissionSource(sellable=sellable, direct_value=Decimal(10), installments_value=1, store=self.store) sale.order() method = PaymentMethod.get_by_name(self.store, u'money') till = Till.get_last_opened(self.store) method.create_inpayment(sale.group, sale.branch, sale.get_sale_subtotal(), till=till) sale.confirm() sale.set_paid() salesperson_name = salesperson.person.name commissions = list(self.store.find(CommissionView)) commissions[0].identifier = 1 commissions[1].identifier = 139 self._diff_expected(SalesPersonReport, 'sales-person-report', commissions, salesperson_name)
def _setup_payment_methods(self, payment_type): methods = PaymentMethod.get_creatable_methods(self.store, payment_type, separate=False) group = None for method in methods: method_name = method.method_name widget = gtk.RadioButton(group, N_(method.description)) widget.connect("toggled", self._on_method__toggled) widget.set_data("method", method) if group is None: group = widget self.methods_box.pack_start(widget, False, False, 6) widget.show() self._methods[method_name] = method self._widgets[method_name] = widget self.method_set_sensitive(method_name, True) # Don't allow the user to change the kind of payment method if # there's only one if len(methods) == 1: self._widgets[methods[0].method_name].set_sensitive(False) else: # Money should be the first widget = self._widgets.get(u"money") if widget is not None: self.methods_box.reorder_child(widget, 0) # Multiple should be the last widget = self._widgets.get(u"multiple") if widget is not None: self.methods_box.reorder_child(widget, len(self.methods_box) - 1)
def _create_payment(self): group = PaymentGroup() group.payer = self.client.person method = PaymentMethod.get_by_name(self.store, u'credit') branch = api.get_current_branch(self.store) if self.model.value < 0: payment_type = Payment.TYPE_IN else: payment_type = Payment.TYPE_OUT # Set status to PENDING now, to avoid calling set_pending on # on_confirm for payments that shoud not have its status changed. payment = Payment(open_date=localtoday(), branch=branch, status=Payment.STATUS_PENDING, description=self.model.description, value=abs(self.model.value), base_value=abs(self.model.value), due_date=localtoday(), method=method, group=group, till=None, category=None, payment_type=payment_type, bill_received=False) payment.pay() return payment
def __init__(self, wizard, previous, store, consignment, outstanding_value=Decimal(0)): self._method = PaymentMethod.get_by_name(store, u'money') BaseWizardStep.__init__(self, store, wizard, previous=None) self._consignment = consignment self._outstanding_value = outstanding_value self._setup_slaves()
def test_installments_commission_amount(self): self._payComissionWhenConfirmed() sale = self.create_sale() sellable = self.add_product(sale, price=300) sale.order() CommissionSource(sellable=sellable, direct_value=12, installments_value=5, store=self.store) method = PaymentMethod.get_by_name(self.store, u'check') method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(100)) method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(200)) self.assertTrue(self.store.find(Commission, sale=sale).is_empty()) sale.confirm() self.assertFalse(self.store.find(Commission, sale=sale).is_empty()) commissions = self.store.find(Commission, sale=sale).order_by(Commission.value) self.assertEquals(commissions.count(), 2) for c in commissions: self.failUnless(c.commission_type == Commission.INSTALLMENTS) # the first payment represent 1/3 of the total amount # 5% of 300: 15,00 * 1/3 => 5,00 self.assertEquals(commissions[0].value, Decimal("5.00")) # the second payment represent 2/3 of the total amount # $15 * 2/3 => 10,00 self.assertEquals(commissions[1].value, Decimal("10.00"))
def _setup_comboboxentry_slave(self, data=None): widget = ProxyComboEntry() widget.props.sensitive = self.sensitive widget.model_attribute = "field_value" widget.data_type = unicode detail = sysparam.get_detail_by_name(self.model.field_name) is_mandatory = not detail.allow_none self._block_none_value = is_mandatory widget.set_property('mandatory', is_mandatory) if not data: field_type = detail.get_parameter_type() # FIXME: DEFAULT_PAYMENT_METHOD needs to filter information from # domain because it cannot be any non-creatable method. # Find a way to implement this in a generic on ParameterDetails if self.model.field_name == "DEFAULT_PAYMENT_METHOD": result = PaymentMethod.get_creatable_methods( self.store, Payment.TYPE_IN, False) else: result = self.store.find(field_type) data = [(res.get_description(), unicode(res.id)) for res in result] widget.prefill(data) self.proxy.add_widget("field_value", widget) self.container.add(widget) widget.show() widget.connect('validation-changed', self._on_entry__validation_changed)
def _auto_confirm_sale_wizard_with_store_credit(self, wizard, app, store, sale, subtotal, total_paid, current_document): sale.client = self._create_client(store) payment_method = PaymentMethod.get_by_name(store, u'store_credit') return self._auto_confirm_sale(wizard, app, store, sale, subtotal, total_paid, payment_method)
def test_sale_payment_reserved(self): sale = self.create_sale() sale.identifier = 12345 self.add_product(sale, price=100) method = PaymentMethod.get_by_name(self.store, u'check') p1 = method.create_payment( Payment.TYPE_IN, sale.group, sale.branch, 50) p2 = method.create_payment( Payment.TYPE_IN, sale.group, sale.branch, 50) for p in [p1, p2]: p.set_pending() p.due_date = localdatetime(2013, 1, 1) # Pay only one payment so there are 50 paid and 50 confirmed # (waiting to be paid) totalizing in 100 that's the total here. p1.pay(paid_date=localdatetime(2013, 1, 2)) total_paid = sale.group.get_total_confirmed_value() self._create_wizard(sale=sale, total_paid=total_paid) self._check_wizard('wizard-sale-payment-reserved') self.assertNotVisible(self.step, ['select_method_holder', 'subtotal_expander']) with mock.patch.object(self.store, 'commit'): self._go_to_next() # Make sure no payments were created self.assertEqual(set(sale.payments), set([p1, p2]))
def testBank(self): sale = self.create_sale() method = PaymentMethod.get_by_name(self.store, self.method_type) payment = method.create_outpayment(sale.group, sale.branch, Decimal(10)) check_data = method.operation.get_check_data_by_payment(payment) check_data.bank_account.bank_number = 123 self.assertEquals(payment.bank_account_number, 123)
def createInPayments(self, no=3): sale = self.create_sale() d = datetime.datetime.today() method = PaymentMethod.get_by_name(self.store, self.method_type) payments = method.create_inpayments(sale.group, sale.branch, Decimal(100), [d] * no) return payments
def _delete_account(self, account_view): store = api.new_store() account = store.fetch(account_view.account) methods = PaymentMethod.get_by_account(store, account) if methods.count() > 0: if not yesno( _('This account is used in at least one payment method.\n' 'To be able to delete it the payment methods needs to be' 're-configured first'), gtk.RESPONSE_NO, _("Configure payment methods"), _("Keep account")): store.close() return elif not yesno( _('Are you sure you want to remove account "%s" ?') % ( (account_view.description, )), gtk.RESPONSE_NO, _("Remove account"), _("Keep account")): store.close() return if account_view.id in self._pages: account_page = self._pages[account_view.id] self._close_page(account_page) self.accounts.remove(account_view) self.accounts.flush() imbalance = api.sysparam(store).IMBALANCE_ACCOUNT for method in methods: method.destination_account = imbalance account.remove(store) store.commit(close=True)
def createOutPayments(self, no=3): purchase = self.create_purchase_order() d = datetime.datetime.today() method = PaymentMethod.get_by_name(self.store, self.method_type) payments = method.create_outpayments(purchase.group, purchase.branch, Decimal(100), [d] * no) return payments
def __init__(self, wizard, parent, store, order, payment_method, outstanding_value=currency(0), finish_on_total=True, allow_remove_paid=True): """ :param finish_on_total: finalize the payment when the total value is reached. """ self._has_modified_payments = False self._allow_remove_paid = allow_remove_paid self.finish_on_total = finish_on_total # We need a temporary object to hold the value that will be read from # the user. We will set a proxy with this temporary object to help # with the validation. self._holder = Settable(value=Decimal(0)) self._wizard = wizard # 'money' is the default payment method and it is always avaliable. self._method = PaymentMethod.get_by_name(store, u'money') BaseEditorSlave.__init__(self, store, order) self._outstanding_value = (outstanding_value or self._get_total_amount()) self._total_value = self._outstanding_value self._setup_widgets() self.register_validate_function(self._refresh_next) self.force_validation()
def undo(self, reason): """Undo this returned sale. This includes removing the returned items from stock again (updating the quantity decreased on the sale). :param reason: The reason for this operation. """ assert self.can_undo() for item in self.get_items(): item.undo() # We now need to create a new in payment for the total amount of this # returned sale. method_name = self._guess_payment_method() method = PaymentMethod.get_by_name(self.store, method_name) description = _(u'%s return undone for sale %s') % ( method.description, self.sale.identifier) payment = method.create_payment(Payment.TYPE_IN, payment_group=self.group, branch=self.branch, value=self.returned_total, description=description) payment.set_pending() payment.pay() self.status = self.STATUS_CANCELLED self.cancel_date = localnow() self.undo_reason = reason # if the sale status is returned, we must reset it to confirmed (only # confirmed sales can be returned) if self.sale.is_returned(): self.sale.set_not_returned()
def test_installments_commission_amount_with_multiple_items(self): self._payComissionWhenConfirmed() sale = self.create_sale() sellable = self.add_product(sale, price=300, quantity=3) sale.order() CommissionSource(sellable=sellable, direct_value=12, installments_value=5, store=self.store) method = PaymentMethod.get_by_name(self.store, u'check') method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(300)) method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(450)) method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(150)) self.assertTrue(self.store.find(Commission, sale=sale).is_empty()) sale.confirm() commissions = self.store.find(Commission, sale=sale).order_by(Commission.value) self.assertEquals(commissions.count(), 3) for c in commissions: self.failUnless(c.commission_type == Commission.INSTALLMENTS) # the first payment represent 1/3 of the total amount # 45 / 6 => 7.50 self.assertEquals(commissions[0].value, Decimal("7.50")) # the second payment represent 1/3 of the total amount # 5% of 900: 45,00 * 1/3 => 15,00 self.assertEquals(commissions[1].value, Decimal("15.00")) # the third payment represent 1/2 of the total amount # 45 / 2 => 22,50 self.assertEquals(commissions[2].value, Decimal("22.50"))
def test_get_total_value(self): method = PaymentMethod.get_by_name(self.store, u'check') # Test for a group in a sale # On sale's group, total value should return # sum(inpayments.value) - sum(outpayments.value) sale = self.create_sale() group = sale.group self.assertEqual(group.get_total_value(), 0) method.create_payment(Payment.TYPE_IN, group, sale.branch, Decimal(100)) self.assertEqual(group.get_total_value(), Decimal(100)) method.create_payment(Payment.TYPE_IN, group, sale.branch, Decimal(200)) self.assertEqual(group.get_total_value(), Decimal(300)) method.create_payment(Payment.TYPE_OUT, group, sale.branch, Decimal(50)) self.assertEqual(group.get_total_value(), Decimal(250)) # Test for a group in a purchase # On purchase's group, total value should return # sum(inpayments.value) - sum(outpayments.value) purchase = self.create_purchase_order() group = purchase.group self.assertEqual(group.get_total_value(), 0) method.create_payment(Payment.TYPE_OUT, group, purchase.branch, Decimal(100)) self.assertEqual(group.get_total_value(), Decimal(100)) method.create_payment(Payment.TYPE_OUT, group, purchase.branch, Decimal(200)) self.assertEqual(group.get_total_value(), Decimal(300)) method.create_payment(Payment.TYPE_IN, group, purchase.branch, Decimal(50)) self.assertEqual(group.get_total_value(), Decimal(250))
def create_model(self, store): group = PaymentGroup() method = PaymentMethod.get_by_name(store, u'credit') 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(), branch=branch, status=Payment.STATUS_PENDING, description=u'', value=currency(0), base_value=currency(0), due_date=None, method=method, group=group, till=None, category=None, payment_type=Payment.TYPE_OUT, bill_received=False)
def get_cash_amount(self): """Returns the total cash amount on the till. That includes "extra" payments (like cash advance, till complement and so on), the money payments and the initial cash amount. :returns: the cash amount on the till :rtype: currency """ store = self.store money = PaymentMethod.get_by_name(store, u'money') clause = And( Or(Eq(TillEntry.payment_id, None), Payment.method_id == money.id), TillEntry.till_id == self.id) join = LeftJoin(Payment, Payment.id == TillEntry.payment_id) results = store.using(TillEntry, join).find(TillEntry, clause) return currency(self.initial_cash_amount + (results.sum(TillEntry.value) or 0))
def undo(self, reason): """Undo this returned sale. This includes removing the returned items from stock again (updating the quantity decreased on the sale). :param reason: The reason for this operation. """ assert self.can_undo() for item in self.get_items(): item.undo() payment = self._get_cancel_candidate_payment(pending_only=True) if payment: # If there are pending out payments which match the returned value # and those payments are all of the same method, we can just cancel any of # these payments right away. payment.cancel() else: # We now need to create a new in payment for the total amount of this # returned sale. payment = self._get_cancel_candidate_payment() method = payment.method if payment else PaymentMethod.get_by_name( self.store, 'money') description = _(u'%s return undone for sale %s') % ( method.description, self.sale.identifier) payment = method.create_payment(Payment.TYPE_IN, payment_group=self.group, branch=self.branch, value=self.returned_total, description=description, ignore_max_installments=True) payment.set_pending() payment.pay() self.status = self.STATUS_CANCELLED self.cancel_date = localnow() self.undo_reason = reason # if the sale status is returned, we must reset it to confirmed (only # confirmed sales can be returned) if self.sale.is_returned(): self.sale.set_not_returned()
def test_pay(self): branch = self.create_branch() group = self.create_payment_group() method = PaymentMethod.get_by_name(self.store, u'bill') payment1 = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(10)) payment2 = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(10)) group.confirm() self.assertEqual(payment1.status, Payment.STATUS_PENDING) self.assertEqual(payment2.status, Payment.STATUS_PENDING) payment2.pay() self.assertEqual(payment2.status, Payment.STATUS_PAID) group.pay() self.assertEqual(payment1.status, Payment.STATUS_PAID) self.assertEqual(payment2.status, Payment.STATUS_PAID)
def test_out_payment_receipt(self): drawee = self.create_supplier() address = self.create_address() address.person = drawee.person method = PaymentMethod.get_by_name(self.store, u'money') group = self.create_payment_group() branch = self.create_branch() payment = method.create_payment(Payment.TYPE_OUT, group, branch, Decimal(100)) payment.description = u"Test payable account" payment.group.recipient = drawee.person payment.set_pending() payment.pay() payment.identifier = 35 date = datetime.date(2012, 1, 1) self._diff_expected(OutPaymentReceipt, 'out-payment-receipt-report', payment, None, date)
def test_in_payment_receipt(self): payer = self.create_client() address = self.create_address() address.person = payer.person method = PaymentMethod.get_by_name(self.store, u'money') group = self.create_payment_group() branch = self.create_branch() payment = method.create_payment(Payment.TYPE_IN, group, branch, Decimal(100)) payment.description = u"Test receivable account" payment.group.payer = payer.person payment.set_pending() payment.pay() payment.identifier = 36 date = datetime.date(2012, 1, 1) self._diff_expected(InPaymentReceipt, 'in-payment-receipt-report', payment, None, date)
def test_installments_commission_amount_when_sale_return(self): if True: raise SkipTest( u"See stoqlib.domain.returnedsale.ReturnedSale.return_ " u"and bug 5215.") self._payComissionWhenConfirmed() sale = self.create_sale() sellable = self.create_sellable() CommissionSource(sellable=sellable, direct_value=12, installments_value=5, store=self.store) sale.add_sellable(sellable, quantity=3, price=300) product = sellable.product branch = get_current_branch(self.store) self.create_storable(product, branch, 100) sale.order() method = PaymentMethod.get_by_name(self.store, u'check') payment1 = method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(300)) payment2 = method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(450)) payment3 = method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(150)) sale.confirm() # the commissions are created after the payment payment1.pay() payment2.pay() payment3.pay() returned_sale = sale.create_sale_return_adapter() returned_sale.return_() self.assertEqual(sale.status, Sale.STATUS_RETURNED) commissions = self.store.find(Commission, sale=sale) value = sum([c.value for c in commissions]) self.assertEqual(value, Decimal(0)) self.assertEqual(commissions.count(), 4) self.assertFalse(commissions[-1].value >= 0)
def test_step_payment_method_card(self): self._create_wizard() self._select_method('card') self._go_to_next() # XXX: The step could provide an api to get the slave. self.step._method_slave.auth_number.update(1234) # Finish the checkout with mock.patch.object(self.store, 'commit'): self._go_to_next() self.assertEquals(self.sale.payments[0].method.method_name, 'card') models = [] operation = PaymentMethod.get_by_name(self.store, u'card').operation for p in self.sale.payments: models.append(operation.get_card_data_by_payment(p)) self._check_wizard('wizard-sale-step-payment-method-card', models)
def trade(self): """Do a trade for this return Almost the same as :meth:`.return_`, but unlike it, this won't generate reversed payments to the client. Instead, it'll generate an inpayment using :obj:`.returned_total` value, so it can be used as an "already paid quantity" on :obj:`.new_sale`. """ assert self.new_sale if self.sale: assert self.sale.can_return() self._clean_not_used_items() store = self.store group = self.group method = PaymentMethod.get_by_name(store, u'trade') description = _(u'Traded items for sale %s') % ( self.new_sale.identifier, ) value = self.returned_total value_as_discount = sysparam.get_bool('USE_TRADE_AS_DISCOUNT') if value_as_discount: self.new_sale.discount_value = self.returned_total else: payment = method.create_payment(Payment.TYPE_IN, group, self.branch, value, description=description) payment.set_pending() payment.pay() self._revert_fiscal_entry() login_user = api.get_current_user(self.store) if self.sale: self.sale.return_(self) if self.sale.branch == self.branch: self.confirm(login_user) else: # When trade items without a registered sale, confirm the # new returned sale. self.confirm(login_user)
def test_booklet_with_sale_pdf(self): due_dates = [ datetime.datetime(2012, 1, 5), datetime.datetime(2012, 2, 5), datetime.datetime(2012, 3, 5), datetime.datetime(2012, 4, 5), datetime.datetime(2012, 5, 5), ] items = [ (u"Batata", 2, decimal.Decimal('10')), (u"Tomate", 3, decimal.Decimal('15.5')), (u"Banana", 1, decimal.Decimal('5.25')), ] client = self.create_client() client.credit_limit = decimal.Decimal('100000') address = self.create_address() address.person = client.person sale = self.create_sale(client=client, branch=get_current_branch(self.store)) for description, quantity, price in items: sellable = self.add_product(sale, price, quantity) sellable.description = description sale.order(self.current_user) method = PaymentMethod.get_by_name(self.store, u'store_credit') method.max_installments = 12 method.create_payments(sale.branch, sale.station, Payment.TYPE_IN, sale.group, value=sale.get_total_sale_amount(), due_dates=due_dates) sale.confirm(self.current_user) sale.identifier = 123 for i, payment in enumerate(sale.group.payments): payment.identifier = 66 + i self._diff_expected(BookletReport, 'booklet-with-sale', sale.group.payments)
def register_payment_methods(store): """Registers the payment methods and creates persistent domain classes associated with them. """ from stoqlib.domain.payment.method import PaymentMethod from stoqlib.domain.payment.operation import get_payment_operation_manager log.info("Registering payment operations") pom = get_payment_operation_manager() log.info("Creating domain objects for payment methods") account = sysparam(store).IMBALANCE_ACCOUNT for operation_name in pom.get_operation_names(): operation = pom.get(operation_name) pm = store.find(PaymentMethod, method_name=operation_name).one() if pm is None: pm = PaymentMethod(store=store, method_name=operation_name, destination_account=account, max_installments=operation.max_installments)
def _create_return_payment(self): money = PaymentMethod.get_by_name(self.store, u'money') description = _(u'Money returned for order %s') % ( self.purchase.identifier, ) value = currency(self.model.paid_value - self.model.received_value) today = localtoday().date() payment = Payment(open_date=today, branch=self.purchase.branch, description=description, value=value, base_value=value, due_date=today, method=money, group=self.purchase.group, category=None, store=self.store, payment_type=Payment.TYPE_IN) payment.set_pending() return payment
def test_is_cancelled(self): method = PaymentMethod.get_by_name(self.store, u'check') payment = Payment(value=currency(100), branch=self.create_branch(), due_date=localnow(), method=method, group=None, category=None, payment_type=Payment.TYPE_OUT, store=self.store) self.failIf(payment.is_cancelled()) payment.set_pending() self.failIf(payment.is_cancelled()) payment.pay() self.failIf(payment.is_cancelled()) payment.cancel() self.failUnless(payment.is_cancelled()) with self.assertRaises(StoqlibError): payment.status = Payment.STATUS_CANCELLED payment.cancel()
def test_credit_editor_cancel(self): method = PaymentMethod.get_by_name(self.store, u'credit') client = self.create_client() group = self.create_payment_group(payer=client.person) payment = self.create_payment(method=method, payment_type=Payment.TYPE_OUT, value=6, group=group) payment.set_pending() payment.pay() editor = CreditEditor(self.store, client) editor.description.set_text('Desc') editor.value.set_text('-4') # Canceling the dialog here does not roll back the transaction because # this is done in the caller. editor.cancel() self.assertEqual(client.credit_account_balance, currency(6))
def create_freight_payment(self, group=None): store = self.store money_method = PaymentMethod.get_by_name(store, u'money') # If we have a transporter, the freight payment will be for him if not group: if self.transporter: recipient = self.transporter.person else: recipient = self.supplier.person group = PaymentGroup(store=store, recipient=recipient) description = _(u'Freight for receiving %s') % (self.identifier, ) payment = money_method.create_payment(Payment.TYPE_OUT, group, self.branch, self.freight_total, due_date=localnow(), description=description) payment.set_pending() return payment
def _create_payments(self, station): nfe_payments = list(self.payments) # FIXME: receive from frontend method = PaymentMethod.get_by_name(store=self.store, name=u"bill") # FIXME: Select method in frontend and add option to split the payment # in more than one duplicate identifier = Payment.get_temporary_identifier(self.store) if not nfe_payments: payment = method.create_payment( branch=self.purchase_order.branch, station=station, payment_type=Payment.TYPE_OUT, payment_group=self.purchase_order.group, value=self.purchase_order.purchase_total, identifier=identifier) payment.status = u'paid' return payment payments = [] for i, item in enumerate(nfe_payments, start=1): identifier = Payment.get_temporary_identifier(self.store) payment = Payment(store=self.store, branch=self.purchase_order.branch, identifier=identifier, value=item.value, base_value=item.value, due_date=item.due_date, status=Payment.STATUS_PAID, group=self.purchase_order.group, method=method, bill_received=True, payment_type=Payment.TYPE_OUT, station=station) payment.description = method.describe_payment( payment.group, i, len(list(nfe_payments))) item.description = payment.description self.purchase_order.group.add_item(payment) payments.append(payment) return payments
def _create_freight_payment(self): store = self.store money_method = PaymentMethod.get_by_name(store, u'money') # If we have a transporter, the freight payment will be for him # (and in another payment group). if self.transporter is not None: group = PaymentGroup(store=store) group.recipient = self.transporter.person else: group = self.purchase.group description = _(u'Freight for purchase %s') % ( self.purchase.identifier, ) payment = money_method.create_payment(Payment.TYPE_OUT, group, self.branch, self.freight_total, due_date=localnow(), description=description) payment.set_pending() return payment
def test_sales_person_report(self): sysparam.set_bool(self.store, 'SALE_PAY_COMMISSION_WHEN_CONFIRMED', True) salesperson = self.create_sales_person() product = self.create_product(price=100) sellable = product.sellable sale = self.create_sale() sale.salesperson = salesperson sale.add_sellable(sellable, quantity=1) self.create_storable(product, get_current_branch(self.store), stock=100) CommissionSource(sellable=sellable, direct_value=Decimal(10), installments_value=1, store=self.store) sale.order(self.current_user) method = PaymentMethod.get_by_name(self.store, u'money') method.create_payment(sale.branch, sale.station, Payment.TYPE_IN, sale.group, sale.get_sale_subtotal()) sale.confirm(self.current_user) sale.group.pay() salesperson = salesperson commissions = list(self.store.find(CommissionView)) commissions[0].identifier = 1 commissions[1].identifier = 139 self._diff_expected(SalesPersonReport, 'sales-person-report', commissions, salesperson) # Also test when there is no salesperson selected self._diff_expected(SalesPersonReport, 'sales-person-report-without-salesperson', commissions, None)
def _setup_payment_methods(self, payment_type): methods = PaymentMethod.get_creatable_methods(self.store, payment_type, separate=False) group = None for method in methods: widget = self._add_method(method, group) if group is None: group = widget if self._no_payments: self._add_method(None, group) if len(methods) == 1: self._default_method = methods[0].method_name else: # Money should be the first widget = self._widgets.get(u'money') if widget is not None: self.methods_box.reorder_child(widget, 0) # Multiple should be the last widget = self._widgets.get(u'multiple') if widget is not None: self.methods_box.reorder_child(widget, len(self.methods_box) - 1) # The default method could not have been passed to the constructor, # or if it was, it could not be active. Fallback to the parameters' # one or money in case it's not active too if (self._default_method is None or self._default_method not in self._widgets): default = api.sysparam.get_object(self.store, "DEFAULT_PAYMENT_METHOD") if default.method_name in self._widgets: self._default_method = default.method_name else: self._default_method = u'money' self._select_default_method()
def test_pay_with_credit(self): client = self.create_client() sale = self.create_sale(client=client) sale.identifier = 1234 sellable = self.create_sellable(price=10) sale.add_sellable(sellable) # Create credit to the client. method = PaymentMethod.get_by_name(self.store, u'credit') group = self.create_payment_group(payer=client.person) payment = self.create_payment(value=20, method=method, group=group) payment.set_pending() payment.pay() self.assertEqual(client.credit_account_balance, 20) editor = SalePaymentsEditor(self.store, sale) # Select credit method. for radio in editor.slave.methods_box.get_children(): if radio.get_label() == 'Credit ($20.00)': radio.set_active(True) break # Add credit payment. editor.slave.value.update(10) self.assertSensitive(editor.slave, ['add_button']) self.click(editor.slave.add_button) editor.confirm() self.assertEqual(client.credit_account_balance, 10) for payment in sale.payments: self.assertEqual(payment.status, Payment.STATUS_PAID) # Test remove payment. self.assertNotSensitive(editor.slave, ['remove_button']) editor = SalePaymentsEditor(self.store, sale) payments = editor.slave.payments payments.select(payments[0]) self.assertSensitive(editor.slave, ['remove_button']) self.click(editor.slave.remove_button) editor.confirm() for payment in sale.payments: self.assertEqual(payment.status, Payment.STATUS_CANCELLED)
def test_cancel(self): branch = self.create_branch() group = self.create_payment_group() method = PaymentMethod.get_by_name(self.store, u'bill') payment1 = method.create_payment(branch, self.current_station, Payment.TYPE_IN, group, Decimal(10)) payment2 = method.create_payment(branch, self.current_station, Payment.TYPE_IN, group, Decimal(10)) payment3 = method.create_payment(branch, self.current_station, Payment.TYPE_IN, group, Decimal(10)) group.confirm() payment3.pay() self.assertEqual(payment1.status, Payment.STATUS_PENDING) self.assertEqual(payment2.status, Payment.STATUS_PENDING) self.assertEqual(payment3.status, Payment.STATUS_PAID) group.cancel() self.assertEqual(payment1.status, Payment.STATUS_CANCELLED) self.assertEqual(payment2.status, Payment.STATUS_CANCELLED) self.assertEqual(payment3.status, Payment.STATUS_PAID)
def testSalesPersonReport(self): sysparam(self.store).SALE_PAY_COMMISSION_WHEN_CONFIRMED = 1 salesperson = self.create_sales_person() product = self.create_product(price=100) sellable = product.sellable sale = self.create_sale() sale.salesperson = salesperson sale.add_sellable(sellable, quantity=1) self.create_storable(product, get_current_branch(self.store), stock=100) CommissionSource(sellable=sellable, direct_value=Decimal(10), installments_value=1, store=self.store) sale.order() method = PaymentMethod.get_by_name(self.store, u'money') till = Till.get_last_opened(self.store) method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, sale.get_sale_subtotal(), till=till) sale.confirm() sale.group.pay() salesperson_name = salesperson.person.name commissions = list(self.store.find(CommissionView)) commissions[0].identifier = 1 commissions[1].identifier = 139 self._diff_expected(SalesPersonReport, 'sales-person-report', commissions, salesperson_name)
def test_get_interest(self): method = PaymentMethod.get_by_name(self.store, u'check') payment = Payment(value=currency(100), branch=self.create_branch(), due_date=localnow(), open_date=localnow(), method=method, group=None, category=None, payment_type=Payment.TYPE_OUT, store=self.store) for day, expected_value in [(0, 0), (-1, 0), (-30, 0), (30, 0)]: payment.due_date = self._get_relative_day(day) self.assertEqual(payment.get_interest(), currency(expected_value)) method.daily_interest = Decimal(1) for day, expected_value in [(0, 0), (-1, 1), (-30, 30), (30, 0)]: payment.due_date = self._get_relative_day(day) self.assertEqual(payment.get_interest(), currency(expected_value)) due_date = self._get_relative_day(-15) paid_date = self._get_relative_day(-5) payment.due_date = payment.open_date = due_date method.daily_interest = Decimal(2) self.assertEqual(payment.get_interest(paid_date.date()), currency(20)) self.assertEqual(payment.get_interest(due_date.date()), currency(0)) for day in (18, -18): paid_date = self._get_relative_day(day) self.assertRaises(ValueError, payment.get_interest, paid_date.date())
def test_installments_commission_amount_with_multiple_items(self): self._payComissionWhenConfirmed() sale = self.create_sale() sellable = self.add_product(sale, price=300, quantity=3) sale.order() CommissionSource(sellable=sellable, direct_value=12, installments_value=5, store=self.store) method = PaymentMethod.get_by_name(self.store, u'check') method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(300)) method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(450)) method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, Decimal(150)) self.assertTrue(self.store.find(Commission, sale=sale).is_empty()) sale.confirm() commissions = self.store.find(Commission, sale=sale).order_by(Commission.value) self.assertEqual(commissions.count(), 3) for c in commissions: self.assertTrue(c.commission_type == Commission.INSTALLMENTS) # the first payment represent 1/3 of the total amount # 45 / 6 => 7.50 self.assertEqual(commissions[0].value, Decimal("7.50")) # the second payment represent 1/3 of the total amount # 5% of 900: 45,00 * 1/3 => 15,00 self.assertEqual(commissions[1].value, Decimal("15.00")) # the third payment represent 1/2 of the total amount # 45 / 2 => 22,50 self.assertEqual(commissions[2].value, Decimal("22.50"))
def test_get_total_to_pay(self): method = PaymentMethod.get_by_name(self.store, u'check') # Test for a group in a sale sale = self.create_sale() group = sale.group self.assertEqual(group.get_total_to_pay(), 0) payment1 = method.create_payment(Payment.TYPE_IN, group, sale.branch, Decimal(100)) payment1.set_pending() self.assertEqual(group.get_total_to_pay(), Decimal(100)) payment2 = method.create_payment(Payment.TYPE_IN, group, sale.branch, Decimal(200)) payment2.set_pending() self.assertEqual(group.get_total_to_pay(), Decimal(300)) payment1.pay() self.assertEqual(group.get_total_to_pay(), Decimal(200)) payment2.pay() self.assertEqual(group.get_total_to_pay(), Decimal(0))
def test_client_with_credit(self): method = PaymentMethod.get_by_name(self.store, u'credit') client_without_credit = self.create_client() client_without_credit.person.name = u'Chico' client_with_credit = self.create_client() client_with_credit.person.name = u'Juca' # Create a client and add some credit for it group = self.create_payment_group(payer=client_with_credit.person) payment = self.create_payment(payment_type=Payment.TYPE_OUT, value=10, method=method, group=group) payment.set_pending() payment.pay() wizard = SaleQuoteWizard(self.store) step = wizard.get_current_step() step.client_gadget.set_value(client_without_credit) self.check_wizard(wizard, 'wizard-salequote-client-without-credit') step.client_gadget.set_value(client_with_credit) self.check_wizard(wizard, 'wizard-salequote-client-with-credit')
def _create_change_payment(self): if self.cash_change_slave.credit_checkbutton.get_active(): method_name = u'credit' else: method_name = u'money' payments_value = self.model.group.get_total_confirmed_value() sale_total = self.model.get_total_sale_amount() # To have reached this far, the payments value must be greater than the # sale total assert payments_value > sale_total, (payments_value, sale_total) method = PaymentMethod.get_by_name(self.store, method_name) description = _(u'%s returned for sale %s') % (method.description, self.model.identifier) payment = method.create_payment(Payment.TYPE_OUT, payment_group=self.model.group, branch=self.model.branch, value=(payments_value - sale_total), description=description) payment.set_pending() if method_name == u'credit': payment.pay()
def _create_sale(self, invoice_number, due_date=None): sale = self.create_sale() sale.invoice_number = invoice_number sale.branch = get_current_branch(self.store) # [0] - Description # [1] - Code # [2] - Price # [3] - Quantity for data in [(u"Laranja", u"1", Decimal(1), Decimal(10)), (u"Limão", u"2", Decimal('0.5'), Decimal(15)), (u"Abacaxi", u"3", Decimal(3), Decimal(1)), (u"Cenoura", u"4", Decimal('1.5'), Decimal(6)), (u"Pêssego", u"5", Decimal('3.5'), Decimal(3))]: sellable = self._create_sellable(data[0], data[1], data[2]) storable = Storable(product=sellable.product, store=self.store) storable.increase_stock(data[3], get_current_branch(self.store), 0, sale.id) sale.add_sellable(sellable, data[3]) sale.client = self.create_client() self._create_address(sale.client.person, street=u"Rua dos Tomates", streetnumber=2666, postal_code=u'87654-321') sale.order() method = PaymentMethod.get_by_name(self.store, u'money') method.create_payment(Payment.TYPE_IN, sale.group, sale.branch, sale.get_sale_subtotal(), due_date=due_date) sale.confirm() return sale
def _create_freight_payment(self): store = self.store money_method = PaymentMethod.get_by_name(store, u'money') # If we have a transporter, the freight payment will be for him # (and in another payment group). purchases = list(self.purchase_orders) if len(purchases) == 1 and self.transporter is None: group = purchases[0].group else: if self.transporter: recipient = self.transporter.person else: recipient = self.supplier.person group = PaymentGroup(store=store, recipient=recipient) description = _(u'Freight for receiving %s') % (self.identifier, ) payment = money_method.create_payment(Payment.TYPE_OUT, group, self.branch, self.freight_total, due_date=localnow(), description=description) payment.set_pending() return payment