Changeset 12734


Ignore:
Timestamp:
11 Mar 2015, 17:08:34 (10 years ago)
Author:
uli
Message:

Restructure things a bit.

Location:
main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/customers/browser.py

    r12724 r12734  
    4343from waeup.ikoba.payments.payment import format_payment_item_values
    4444from waeup.ikoba.payments.interfaces import (
    45     IPaymentGatewayServicesLister, IPaymentGatewayService, IPayer
     45    IPaymentGatewayServicesLister, IPaymentGatewayService, IPayer, IPayable
    4646    )
    4747from waeup.ikoba.widgets.hrefwidget import HREFDisplayWidget
     
    5656from waeup.ikoba.customers.catalog import search
    5757from waeup.ikoba.customers.workflow import PAYMENT_TRANSITIONS
    58 from waeup.ikoba.customers.contracts import payment_items_from_contract
    5958
    6059
     
    15281527                return
    15291528            payer = IPayer(self.context)
    1530             payment_items = payment_items_from_contract(self.context)
    1531             payment = service.create_payment(payer, payment_items)
    1532             payment.payable_id = self.context.contract_id
     1529
     1530            payable = IPayable(self.context)
     1531            payment = service.create_payment(payer, payable)
     1532            service.store(payment)
    15331533            payment, view_name = service.next_step(payment.payment_id)
    15341534            url = self.url(payment, view_name)
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/customers/contracts.py

    r12728 r12734  
    286286    @property
    287287    def title(self):
    288         return self.context.title
     288        return self.context.title or u''
    289289
    290290    @property
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/demo_provider.py

    r12723 r12734  
    22from waeup.ikoba.interfaces import MessageFactory as _
    33from waeup.ikoba.browser.layout import IkobaEditFormPage, action
    4 from waeup.ikoba.payments.interfaces import IPaymentGatewayService, IPayment
     4from waeup.ikoba.payments.interfaces import (
     5    IPaymentGatewayService, IPayment, IPayable, IPayer, IPayee,)
    56from waeup.ikoba.payments.payment import (
    6     Payment, get_payment, find_payable_from_payable_id)
     7    Payment, get_payment, find_payable_from_payable_id,
     8    PaymentProviderServiceBase)
    79
    810
     
    1012
    1113
    12 class DemoCreditcardPaymentService(grok.GlobalUtility):
     14class DemoCreditcardPaymentService(PaymentProviderServiceBase):
    1315    """A demo payment gateway service.
    1416
     
    2022    title = _(u'Credit Card (Demo Payments)')
    2123
    22     def create_payment(self, payer, payment_item_list=[],  payee=None):
     24    def create_payment(self, payer, payable,  payee=None):
    2325        """Create a payment.
    2426        """
    25         payment = Payment()
     27        if not IPayer.providedBy(payer):
     28            payer = IPayer(payer)
     29        if not IPayable.providedBy(payable):
     30            payable = IPayable(payable)
     31        if (payee is not None) and (not IPayee.providedBy(payee)):
     32            payee = IPayee(payee)
     33        payment = Payment(payer, payable, payee)
    2634        payment.gateway_service = 'demo_creditcard'  # must be grok.name above
    27         payment.payer_id = payer.payer_id
    28         # XXX: we should not have to store a payment before adding items
    29         site = grok.getSite()
    30         site['payments'][payment.payment_id] = payment
    31         for item in payment_item_list:
    32             payment.add_payment_item(item)
    3335        return payment
    3436
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/interfaces.py

    r12726 r12734  
    2020from zope import schema
    2121from zope.component import getUtilitiesFor
    22 from zope.container.interfaces import IContainer
    23 from zope.container.constraints import contains
    2422from zope.interface import Interface, Attribute
    2523from waeup.ikoba.interfaces import (
     
    8886        )
    8987
    90     def create_payment(payer, payment_item_list, payee):
     88    def create_payment(payer, payable, payee):
    9189        """Create a payment.
    9290
    9391        For all parameters we expect an object, that implements
    94         `IPayer`, `IPaymentItem`, or `IPayee` respectively. If not,
     92        `IPayer`, `IPayable`, or `IPayee` respectively. If not,
    9593        then the given objects must be at least adaptable to the
    9694        respective interface.
     
    106104
    107105        May result in (None, None).
     106        """
     107
     108    def store(payment):
     109        """Store `payment` in site.
    108110        """
    109111
     
    232234
    233235
    234 class IPayment(IContainer, IIkobaObject):
     236class IPayment(IIkobaObject):
    235237    """A base representation of payments.
    236238
     
    256258    we mark the payment 'failed'.
    257259    """
    258     contains(IPaymentItem)
    259 
    260260    payment_id = schema.TextLine(
    261261        title=u'Payment Identifier',
    262262        default=None,
     263        required=True,
     264        )
     265
     266    title = schema.TextLine(
     267        title=u'Payment description.',
     268        default=u'',
    263269        required=True,
    264270        )
     
    333339        """
    334340
    335     def add_payment_item(item):
    336         """Payments contain payment items.
    337 
    338         Add one
    339         """
    340 
    341341
    342342class IPayer(Interface):
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/payment.py

    r12721 r12734  
    101101
    102102class PaymentProviderServiceBase(grok.GlobalUtility):
    103 
     103    """Base for IPaymentGatewayServices.
     104    """
    104105    grok.baseclass()
    105106    grok.implements(IPaymentGatewayService)
     
    107108    title = u'Sample Credit Card Service'
    108109
    109 
    110 @attrs_to_fields
    111 class Payment(grok.Container, Logger):
     110    def store(self, payment):
     111        """Store `payment` in site.
     112        """
     113        site = grok.getSite()
     114        payments = site['payments']
     115        if payment.payment_id in payments:
     116            del site['payments'][payment.payment_id]
     117        site['payments'][payment.payment_id] = payment
     118
     119
     120@attrs_to_fields
     121class Payment(grok.Model, Logger):
    112122    """This is a payment.
    113123    """
     
    119129    logger_format_str = '"%(asctime)s","%(user)s",%(message)s'
    120130
    121     @property
    122     def amount(self):
    123         """The amount of a payment.
    124 
    125         Equals the sum of items contained.
    126         """
    127         return sum(
    128             [item.amount for item in self.values()],
    129             decimal.Decimal("0.00")  # default value
    130         )
    131 
    132     def __init__(self):
     131    def __init__(self, payer, payable, payee=None):
    133132        super(Payment, self).__init__()
     133        item_amounts = [decimal.Decimal("0.00"), ]
     134        item_amounts += [item.amount for item in payable.payment_items]
     135        self.amount = sum(item_amounts)
     136        self.payer_id = payer.payer_id
     137        self.payable_id = payable.payable_id
     138        self.title = payable.title
    134139        self.creation_date = datetime.utcnow()
    135140        self.payment_date = None
    136141        self.payment_id = u'PAY_' + unicode(uuid.uuid4().hex)
    137142        self.state = STATE_UNPAID
     143        self.currency = payable.currency
     144        if payee is not None:
     145            self.payee_id = payee.payee_id
    138146        return
    139147
     
    162170        notify(grok.ObjectModifiedEvent(self))
    163171
    164     def add_payment_item(self, item):
    165         """Add `item`
    166 
    167         Returns the key under which the `item` was stored. Please do
    168         not make anby assumptions about the key. It will be a
    169         string. That is all we can tell.
    170 
    171         """
    172         cnt = 0
    173         while str(cnt) in self:
    174             cnt += 1
    175         self[str(cnt)] = item
    176         return str(cnt)
    177 
    178172
    179173@attrs_to_fields
     
    187181
    188182@attrs_to_fields
    189 class PaymentItem(grok.Model):
     183class PaymentItem(object):
    190184
    191185    grok.implements(IPaymentItem)
    192186
    193     def __init__(self):
     187    def __init__(
     188            self, item_id=u"0", title=u"", amount=decimal.Decimal("0.00")):
    194189        super(PaymentItem, self).__init__()
     190        self.item_id = item_id
     191        self.title = title
     192        self.amount = amount
    195193
    196194
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/paypal.py

    r12724 r12734  
    3535    IPayment, IPaymentGatewayService, IPayer, IPaymentItem, IPayee,
    3636    )
     37from waeup.ikoba.payments.payment import PaymentProviderServiceBase
    3738from waeup.ikoba.payments.paypal_countries import COUNTRIES_VOCAB
    3839from waeup.ikoba.payments.paypal_currencies import CURRENCIES_VOCAB
     
    11411142
    11421143
    1143 class PayPalCreditCardService(grok.GlobalUtility):
     1144class PayPalCreditCardService(PaymentProviderServiceBase):
    11441145    grok.implements(IPaymentGatewayService)
    11451146    grok.name('paypal_creditcard')
     
    12201221
    12211222
    1222 class PayPalRegularPaymentService(grok.GlobalUtility):
     1223class PayPalRegularPaymentService(PaymentProviderServiceBase):
    12231224    grok.implements(IPaymentGatewayService)
    12241225    grok.name('paypal_regular')
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/tests/test_demo_provider.py

    r12712 r12734  
    1 import decimal
    21from zope.component import queryUtility
    32from zope.component.hooks import setSite
     
    109    )
    1110from waeup.ikoba.app import Company
    12 from waeup.ikoba.payments.payment import Payer, PaymentItem, Payee, Payment
     11from waeup.ikoba.payments.payment import Payer, Payee, Payment
    1312from waeup.ikoba.payments.demo_provider import (
    1413    DemoCreditcardPaymentService,
    1514    )
     15from waeup.ikoba.payments.tests.test_payment import FakePayer, FakePayable
    1616
    1717
     
    3535        # we can get payments from payment gateways
    3636        service = DemoCreditcardPaymentService()
    37         payer, payment_item, payee = Payer(), PaymentItem(), Payee()
     37        payer, payable, payee = Payer(), FakePayable(), Payee()
    3838        payer.payer_id = u'SOME_PAYER_ID'
    39         result = service.create_payment(payer, [], payee)
     39        result = service.create_payment(payer, payable, payee)
    4040        assert IPayment.providedBy(result)
    4141        assert result.gateway_service == u'demo_creditcard'
    4242        assert result.state == STATE_UNPAID
    43         assert len(result) == 0  # no items stored
    44 
    45     def test_create_payment_honors_payment_item(self):
    46         # we inspect payment items and take their values
    47         service = DemoCreditcardPaymentService()
    48         payer, payment_item, payee = Payer(), PaymentItem(), Payee()
    49         payment_item.item_id = u'SOME_ITEM_ID'
    50         payer.payer_id = u'SOME_PAYER_ID'
    51         payment_item.amount = decimal.Decimal("300.99")
    52         result = service.create_payment(payer, [payment_item], payee)
    53         self.assertEqual(result.amount, payment_item.amount)
    54         assert len(result) == 1
    5543
    5644    def test_create_payment_honors_payer(self):
    5745        # we inspect payers when creating their payments
    5846        service = DemoCreditcardPaymentService()
    59         payer, payment_item, payee = Payer(), PaymentItem(), Payee()
    60         payment_item.item_id = u'SOME_ITEM_ID'
     47        payer, payable, payee = Payer(), FakePayable(), Payee()
    6148        payer.payer_id = u'SOME_PAYER_ID'
    62         result = service.create_payment(payer, [payment_item], payee)
     49        result = service.create_payment(payer, payable, payee)
    6350        assert result.payer_id == payer.payer_id
    6451
     
    7865        # we are redirected to 'index' in the beginning
    7966        service = DemoCreditcardPaymentService()
    80         p1 = Payment()
     67        p1 = Payment(FakePayer(), FakePayable())
    8168        self.app['payments']['1'] = p1
    8269        p_id = p1.payment_id
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/tests/test_payment.py

    r12719 r12734  
    2424    )
    2525from zope.component.hooks import setSite
    26 from zope.interface import implements
     26from zope.interface import implements, implementer
    2727from zope.interface.verify import verifyClass, verifyObject
    2828from waeup.ikoba.payments.interfaces import (
    2929    IPayment, STATE_UNPAID, STATE_PAID, STATE_FAILED,
    3030    IPaymentGatewayService, IPaymentItem, IPaymentGatewayServicesLister,
    31     IPayableFinder,
     31    IPayableFinder, IPayable, IPayer,
    3232    )
    3333from waeup.ikoba.app import Company
     
    3737    )
    3838from waeup.ikoba.testing import (FunctionalLayer, FunctionalTestCase)
     39
     40
     41@implementer(IPayer)
     42class FakePayer(object):
     43
     44    def __init__(
     45        self, payer_id=u'PAYER_01', first_name=u'Anna', last_name='Tester'):
     46        self.payer_id = payer_id
     47        self.first_name = first_name
     48        self.last_name = last_name
     49
     50
     51FAKE_PAYMENT_ITEMS = (
     52    PaymentItem(u'ITEM1', u'Item title 1', decimal.Decimal("1.00")),
     53    PaymentItem(u'ITEM2', u'Item title 2', decimal.Decimal("2.2")),
     54    )
     55
     56
     57@implementer(IPayable)
     58class FakePayable(object):
     59
     60    payable_id = u'id1'
     61    items = (
     62        (u'item 1', decimal.Decimal("1.00")),
     63        (u'item 2', decimal.Decimal("2.12")),
     64        )
     65
     66    def __init__(self, payable_id=u'PAYABLE_01', title=u'title',
     67                 currency=u'USD', payment_items=FAKE_PAYMENT_ITEMS):
     68        self.payable_id = payable_id
     69        self.title = title
     70        self.currency = currency
     71        self.payment_items = payment_items
    3972
    4073
     
    103136        assert len(util()) > 0
    104137
    105     def test_add_payment_item(self):
    106         # we can add payment items
    107         p1 = Payment()
    108         item1 = PaymentItem()
    109         result = p1.add_payment_item(item1)
    110         assert len(p1) == 1  # do not make assumptions about result content
    111         assert isinstance(result, basestring)
    112 
    113138    def test_get_payment(self):
    114139        # we can lookup payments.
     
    116141        app = self.getRootFolder()['app']
    117142        setSite(app)
    118         p1 = Payment()
    119         item1 = PaymentItem()
     143        p1 = Payment(FakePayer(), FakePayable())
    120144        app['payments']['1'] = p1
    121         p1.add_payment_item(item1)
    122145        p_id = p1.payment_id
    123146        result = get_payment(p_id)
     
    155178
    156179
    157 class PaymentTests(unittest.TestCase):
     180class PaymentTests(FunctionalTestCase):
     181
     182    layer = FunctionalLayer
     183
     184    def setUp(self):
     185        super(PaymentTests, self).setUp()
     186        self.payer = FakePayer()
     187        self.payable = FakePayable()
    158188
    159189    def test_iface(self):
    160190        # Payments fullfill any interface contracts
    161         obj = Payment()
     191        obj = Payment(self.payer, self.payable)
    162192        verifyClass(IPayment, Payment)
    163193        verifyObject(IPayment, obj)
    164194
     195    def test_initial_values(self):
     196        # important attributes are set initially
     197        payer = self.payer
     198        payer.payer_id = u'PAYER_ID'
     199        payable = self.payable
     200        payable.payable_id = u'PAYABLE_ID'
     201        payable.title = u'PAYABLE-TITLE'
     202        payable.currency = 'NGN'
     203        payment = Payment(payer, payable)
     204        assert payment.payer_id == u'PAYER_ID'
     205        assert payment.payable_id == u'PAYABLE_ID'
     206        assert payment.title == u'PAYABLE-TITLE'
     207        assert payment.currency == 'NGN'
     208        assert isinstance(payment.creation_date, datetime.datetime)
     209        assert payment.payment_date is None
     210
    165211    def test_payment_id_unique(self):
    166212        # we get unique payment ids
    167         p1, p2 = Payment(), Payment()
     213        p1 = Payment(self.payer, self.payable)
     214        p2 = Payment(self.payer, self.payable)
    168215        id1, id2 = p1.payment_id, p2.payment_id
    169216        assert id1 != id2
     
    171218    def test_payment_id_format(self):
    172219        # payment ids have a special format: "PAY_<32 hex digits>"
    173         id1 = Payment().payment_id
     220        id1 = Payment(self.payer, self.payable).payment_id
    174221        assert isinstance(id1, basestring)
    175222        assert re.match('PAY_[0-9a-f]{32}', id1)
     
    177224    def test_initial_state_is_unpaid(self):
    178225        # the initial state of payments is <unpaid>
    179         p1 = Payment()
     226        p1 = Payment(self.payer, self.payable)
    180227        assert p1.state == STATE_UNPAID
    181228
    182229    def test_approve(self):
    183230        # we can approve payments
    184         p1 = Payment()
     231        p1 = Payment(self.payer, self.payable)
    185232        p1.approve()
    186233        assert p1.state == STATE_PAID
     
    190237    def test_approve_datetime_given(self):
    191238        # we can give a datetime
    192         p1 = Payment()
     239        p1 = Payment(self.payer, self.payable)
    193240        some_datetime = datetime.datetime(2014, 1, 1, 0, 0, 0)
    194241        p1.approve(payment_date=some_datetime)
     
    198245        # if we do not give a datetime, current one will be used
    199246        current = datetime.datetime.utcnow()
    200         p1 = Payment()
     247        p1 = Payment(self.payer, self.payable)
    201248        p1.approve()
    202249        assert p1.payment_date >= current
     
    204251    def test_mark_failed(self):
    205252        # we can mark payments as failed
    206         p1 = Payment()
     253        p1 = Payment(self.payer, self.payable)
    207254        p1.mark_failed()
    208255        assert p1.state == STATE_FAILED
    209256
    210     def test_add_payment_item(self):
    211         # we can add payment items
    212         p1 = Payment()
    213         item1 = PaymentItem()
    214         result = p1.add_payment_item(item1)
    215         assert len(p1) == 1  # do not make assumptions about result content
    216         assert isinstance(result, basestring)
    217 
    218     def test_add_payment_item_multiple(self):
    219         # we can add several items
    220         p1 = Payment()
    221         item1 = PaymentItem()
    222         item2 = PaymentItem()
    223         result1 = p1.add_payment_item(item1)
    224         result2 = p1.add_payment_item(item2)
    225         assert len(p1) == 2  # do not make assumptions about result content
    226         assert isinstance(result1, basestring)
    227         assert isinstance(result2, basestring)
    228 
    229257    def test_amount(self):
    230258        # the amount of a payment is the sum of amounts of its items
    231         p1 = Payment()
    232         item1 = PaymentItem()
    233         item2 = PaymentItem()
    234         p1.add_payment_item(item1)
    235         p1.add_payment_item(item2)
    236         item1.amount = decimal.Decimal("12.25")
    237         item2.amount = decimal.Decimal("0.5")
     259        payable = self.payable
     260        payable.payment_items[0].amount = decimal.Decimal("12.25")
     261        payable.payment_items[1].amount = decimal.Decimal("0.5")
     262        p1 = Payment(self.payer, self.payable)
    238263        assert p1.amount == decimal.Decimal("12.75")
    239264
    240265    def test_amount_negative(self):
    241266        # we can sum up negative numbers
    242         p1 = Payment()
    243         item1 = PaymentItem()
    244         item2 = PaymentItem()
    245         p1.add_payment_item(item1)
    246         p1.add_payment_item(item2)
    247         item1.amount = decimal.Decimal("2.21")
    248         item2.amount = decimal.Decimal("-3.23")
     267        payable = self.payable
     268        payable.payment_items[0].amount = decimal.Decimal("2.21")
     269        payable.payment_items[1].amount = decimal.Decimal("-3.23")
     270        p1 = Payment(self.payer, payable)
    249271        assert p1.amount == decimal.Decimal("-1.02")
    250272
    251273    def test_amount_empty(self):
    252         # the amount of zero items is 0.00.
    253         p1 = Payment()
    254         assert p1.amount == decimal.Decimal("0.00")
    255         assert isinstance(p1.amount, decimal.Decimal)
     274        # the amount of zero items is None.
     275        payable = FakePayable(payment_items=())
     276        p1 = Payment(self.payer, payable)
     277        self.assertEqual(p1.amount, decimal.Decimal("0.00"))
    256278
    257279
  • main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/payments/tests/test_paypal.py

    r12498 r12734  
    11741174    def test_store_credit_card_invalid(self):
    11751175        # an exception is raised with invalid credit cards.
    1176         site = self.create_site()
     1176        self.create_site()
    11771177        service = PayPalCreditCardService()
    11781178        credit_card = CreditCard(
     
    12031203        # we can actually create payments
    12041204        service = PayPalCreditCardService()
    1205         site = self.create_site()
     1205        self.create_site()
    12061206        credit_card = self.get_credit_card()
    12071207        result = service.store_credit_card(credit_card)
Note: See TracChangeset for help on using the changeset viewer.