def get_value(self, data): amount = super(MoneyField, self).get_value(data) currency = data.get(get_currency_field_name(self.field_name), None) if currency: return Money(amount, currency) return amount
INTEGER_FIELD = ModelWithVanillaMoneyField._meta.get_field('integer') def get_args(value, field): """ Constructs arguments for `display_for_field`. """ if VERSION < (1, 9): return value, field return value, field, '' @pytest.mark.parametrize( 'value, kwargs, expected', ( (Money(10, 'RUB'), {}, '10.00 руб.'), # Issue 232 (Money(1234), { 'USE_L10N': True, 'USE_THOUSAND_SEPARATOR': True }, '1,234.00 XYZ'), # Issue 220 (Money(1000, 'SAR'), { 'USE_I18N': True, 'LANGUAGE_CODE': 'en-us' }, 'ر.س1,000.00'), # Issue 196 (Money(1000, 'PLN'), {}, '1,000.00 zł'), # Issue 102 (Money('3.33', 'EUR'), { 'USE_I18N': True, 'LANGUAGE_CODE': 'de-de' }, '3.33 €'), # Issue 90 )) def test_display_for_field(settings, value, kwargs, expected):
def test_creation(self): o = Operation('deposit', 100) self.assertIsInstance(o, Operation) self.assertEqual(o.amount, Money(100, settings.CURRENCY))
def _zero_balance(self): """Get a balance for this account with all currencies set to zero""" return Balance([Money("0", currency) for currency in self.currencies])
def test_failed_second_intent_succeeds(self): with mock.patch( 'stripe.Webhook.construct_event', return_value=MockEvent( 'payment_intent.payment_failed', {'object': {'id': self.intent.intent_id}} ) ): response = self.client.post( self.webhook, HTTP_STRIPE_SIGNATURE='some signature' ) self.assertEqual(response.status_code, status.HTTP_200_OK) # Stripe might send double failed webhooks response = self.client.post( self.webhook, HTTP_STRIPE_SIGNATURE='some signature' ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.intent.refresh_from_db() payment = self.intent.payment donation = Donation.objects.get(pk=self.donation.pk) self.assertEqual(donation.status, 'failed') self.assertEqual(payment.status, 'failed') self.donation.refresh_from_db() self.assertEqual(self.donation.status, 'failed') second_intent = StripePaymentIntentFactory.create(donation=self.donation, intent_id='some-other-id') with open('bluebottle/funding_stripe/tests/files/intent_webhook_success.json') as hook_file: data = json.load(hook_file) data['object']['id'] = second_intent.intent_id transfer = stripe.Transfer(data['object']['charges']['data'][0]['transfer']) transfer.update({ 'id': data['object']['charges']['data'][0]['transfer'], 'amount': 2500, 'currency': 'eur' }) with mock.patch( 'stripe.Webhook.construct_event', return_value=MockEvent( 'payment_intent.succeeded', data ) ): with mock.patch( 'stripe.Transfer.retrieve', return_value=transfer ): response = self.client.post( self.webhook, HTTP_STRIPE_SIGNATURE='some signature' ) self.assertEqual(response.status_code, status.HTTP_200_OK) second_intent.refresh_from_db() self.assertEqual(second_intent.payment.pk, payment.pk) payment.refresh_from_db() donation.refresh_from_db() self.assertEqual(donation.status, 'succeeded') self.assertEqual(donation.payout_amount, Money(25, 'EUR')) self.assertEqual(payment.status, 'succeeded')
class TestMoneyField: if IS_DRF_3: from rest_framework.fields import empty else: empty = None def get_serializer(self, model_class, instance=None, data=empty): if IS_DRF_3: class Serializer(serializers.ModelSerializer): class Meta: model = model_class fields = '__all__' else: class Serializer(serializers.ModelSerializer): class Meta: model = model_class return Serializer(instance=instance, data=data) @pytest.mark.parametrize( 'model_class, create_kwargs, expected', ( (NullMoneyFieldModel, {'field': None}, {'field': None, 'field_currency': 'USD'}), ( NullMoneyFieldModel, {'field': Money(10, 'USD')}, {'field': '10.00' if IS_DRF_3 else 10, 'field_currency': 'USD'} ), ( ModelWithVanillaMoneyField, {'money': Money(10, 'USD')}, { 'integer': 0, 'money': '10.00' if IS_DRF_3 else 10, 'money_currency': 'USD', 'second_money': '0.00' if IS_DRF_3 else 0, 'second_money_currency': 'EUR'} ), ) ) def test_to_representation(self, model_class, create_kwargs, expected): instance = model_class.objects.create(**create_kwargs) expected['id'] = instance.id serializer = self.get_serializer(model_class, instance=instance) assert serializer.data == expected @pytest.mark.parametrize( 'model_class, field, value, expected', ( (NullMoneyFieldModel, 'field', None, None), (NullMoneyFieldModel, 'field', Money(10, 'USD'), Money(10, 'USD')), (ModelWithVanillaMoneyField, 'money', Money(10, 'USD'), Money(10, 'USD')), (ModelWithVanillaMoneyField, 'money', 10, Money(10, 'XYZ')), ) ) def test_to_internal_value(self, model_class, field, value, expected): serializer = self.get_serializer(model_class, data={field: value}) assert serializer.is_valid() instance = serializer.save() assert getattr(instance, field) == expected def test_invalid_value(self): serializer = self.get_serializer(ModelWithVanillaMoneyField, data={'money': None}) assert not serializer.is_valid() error_text = 'This field may not be null.' if IS_DRF_3 else 'This field is required.' assert serializer.errors == {'money': [error_text]} @pytest.mark.parametrize( 'body, expected', ( ({'field': '10', 'field_currency': 'EUR'}, Money(10, 'EUR')), ({'field': '12.20', 'field_currency': 'GBP'}, Money(12.20, 'GBP')), ({'field': '15.15', 'field_currency': 'USD'}, Money(15.15, 'USD')), ), ) def test_post_put_values(self, body, expected): serializer = self.get_serializer(NullMoneyFieldModel, data=body) serializer.is_valid() if IS_DRF_3: assert serializer.validated_data['field'] == expected else: assert Money(serializer.data['field'], serializer.data['field_currency']) == expected
def __init__(self, client: str, currency: str = settings.CURRENCY): self.currency = currency self.client = client self.current_balance = Money(0, self.currency) self.operations = deque()
def test_it_should_compute_the_account_balance_in_multiple_currencies(self): account = Account.objects.create(owner=self.user, currency='CHF') Charge.objects.create(account=account, amount=Money(10, 'CHF'), product_code='ACHARGE') Charge.objects.create(account=account, amount=Money(-3, 'EUR'), product_code='ACREDIT') with self.assertNumQueries(2): assert account.balance() == Total(-10, 'CHF', 3, 'EUR')
def test_it_can_create_charge_with_both_ad_hoc_label_and_product_code(self): charge = Charge.objects.create(account=self.account, amount=Money(10, 'CHF'), product_code='ACHARGE', ad_hoc_label='hai') charge.full_clean()
def test_objects_creation(self): SimpleModel.objects.create(money=Money("100.0", 'USD')) self.assertEqual(SimpleModel.objects.count(), 1)
def test_payments_should_ignore_refunds(self): Transaction.objects.create(account=self.account, success=True, amount=Money(-10, 'CHF')) with self.assertNumQueries(1): qs = Transaction.successful.payments() assert not qs.exists()
def testSaving(self): somemoney = Money("100.0") model = ModelWithVanillaMoneyField(money=somemoney) model.save() retrieved = ModelWithVanillaMoneyField.objects.get(pk=model.pk) self.assertEquals(somemoney.currency, retrieved.money.currency) self.assertEquals(somemoney, retrieved.money) # Try setting the value directly retrieved.money = Money(1, moneyed.DKK) retrieved.save() retrieved = ModelWithVanillaMoneyField.objects.get(pk=model.pk) self.assertEquals(Money(1, moneyed.DKK), retrieved.money) object = BaseModel.objects.create() self.assertEquals(Money(0, 'USD'), object.first_field) object = BaseModel.objects.create(first_field='111.2') self.assertEquals(Money('111.2', 'USD'), object.first_field) object = BaseModel.objects.create(first_field=Money('123', 'PLN')) self.assertEquals(Money('123', 'PLN'), object.first_field) object = ModelWithDefaultAsDecimal.objects.create() self.assertEquals(Money('0.01', 'CHF'), object.money) object = ModelWithDefaultAsInt.objects.create() self.assertEquals(Money('123', 'GHS'), object.money) object = ModelWithDefaultAsString.objects.create() self.assertEquals(Money('123', 'PLN'), object.money) object = ModelWithDefaultAsStringWithCurrency.objects.create() self.assertEquals(Money('123', 'USD'), object.money) object = ModelWithDefaultAsFloat.objects.create() self.assertEquals(Money('12.05', 'PLN'), object.money) object = ModelWithDefaultAsMoney.objects.create() self.assertEquals(Money('0.01', 'RUB'), object.money)
def it_should_transform_money_to_netaxept_representation(): money = Money(10, 'NOK') assert _money_to_netaxept_amount(money) == 1000 assert _money_to_netaxept_currency(money) == 'NOK'
def current_value(fundname, num_shares): # returns user's money position of given fund quote = getQuote(fundname) price = Money(quote, currency='USD') value = num_shares * price return value
def test_it_should_compute_the_invoice_due_when_there_are_transactions(self): invoice = Invoice.objects.create(account=self.account, due_date=date.today()) Charge.objects.create(account=self.account, invoice=invoice, amount=Money(10, 'CHF'), product_code='ACHARGE') Transaction.objects.create(account=self.account, invoice=invoice, amount=Money(8, 'CHF'), success=True) with self.assertNumQueries(2): assert invoice.due() == Total(2, 'CHF')
def test_it_must_have_ad_hoc_code_or_product_code(self): charge = Charge.objects.create(account=self.account, amount=Money(10, 'CHF')) with raises(ValidationError): charge.full_clean()
def testNonExistentCurrency(self): m = Money(Decimal(10), moneyed.EUR) form = MoneyForm({"money_0": m.amount, "money_1": m.currency}) self.assertFalse(form.is_valid())
def test_it_can_mark_charge_as_deleted(self): Charge.objects.create(account=self.account, amount=Money(10, 'CHF'), product_code='ACHARGE', deleted=True)
def setUp(self): self.mortgage_payment = Money(amount=666.00, currency="USD")
def test_uninvoiced_paymnents_should_return_uninvoiced_payment(self): Transaction.objects.create(account=self.account, success=True, amount=Money(10, 'CHF')) with self.assertNumQueries(1): qs = Transaction.successful.uninvoiced(account_id=self.account.pk).payments() assert qs.exists()
class AssignmentForm(forms.Form): title = _("Assignment") layout = hg.BaseElement( _layout.datatable.DataTable( columns=( _layout.datatable.DataTableColumn(_("Date"), hg.C("row.date"), None), _layout.datatable.DataTableColumn(_("Note"), hg.C("row.note"), None), _layout.datatable.DataTableColumn(_("Account"), hg.C("row.debitaccount"), None), _layout.datatable.DataTableColumn(_("Cost Center"), hg.C("row.creditaccount"), None), _layout.datatable.DataTableColumn( _("Person Number"), hg.C("row.person.personnumber"), None), _layout.datatable.DataTableColumn(_("Donor Number"), hg.C("row.donornumber"), None), _layout.datatable.DataTableColumn( _("Assignment state"), hg.If( hg.C("row.person"), _layout.icon.Icon( "checkmark--filled", size=16, style="fill: currentColor; color: green;", ), _layout.icon.Icon( "warning", size=16, style="fill: currentColor; color: red;", ), ), None, ), _layout.datatable.DataTableColumn(_("Amount"), hg.C("row.amount_formatted"), None), ), row_iterator=hg.C("contributions"), ).with_toolbar(_("Overview of contributions to import"), hg.C("importfile")), hg.DIV( hg.DIV( hg.DIV( hg.F(lambda c: len(c["contributions"])), " ", _("contributions"), _class="bx--batch-summary", ), hg.DIV( _("Sum"), hg.SPAN("|", style="margin-left: 1rem; margin-right: 1rem"), hg.F(lambda c: sum([ convert_money( Money(contr.amount, contr.currency), global_preferences["contributions__currency"], ) for contr in c["contributions"] ]) or Money(0, global_preferences["contributions__currency" ])), style= "position: absolute; right: 0; margin-right: 1rem; color: #ffffff", ), _class="bx--batch-actions--active bx--batch-actions", ), _class="bx--table-toolbar", style="margin-bottom: 4rem", ), hg.DIV(style="margin-bottom: 2rem"), hg.If( hg.C("unassigned_contributions"), _layout.notification.InlineNotification( hg.BaseElement( hg.F(lambda c: len([ c for c in c.get("contributions", ()) if not c.person ])), _(" contributions could not been assigned"), ), _("Please make sure that each entry has contributor number " "which matches with a person number and do the import again" ), kind="error", lowcontrast=True, action=( _("Cancel import"), "window.location = window.location.pathname + '?reset=1'", ), style="max-width: 100%", ), _layout.notification.InlineNotification( _("Assignment complete"), _("Continue in order to complete the import"), kind="success", style="max-width: 100%", ), ), )
def test_it_can_reverse(self): the_charge = Charge.objects.create(account=self.account, amount=Money(10, 'CHF'), product_code='ACHARGE') Charge.objects.create(account=self.account, amount=Money(-10, 'CHF'), product_code='REVERSAL', reverses=the_charge)
def assign_funds_to_invoice(invoice_id: str) -> bool: """ Uses the available funds on the account (credits and payments) to pay the given invoice. :param invoice_id: The id of the invoice. :return: True if the invoice status is paid. A lot of side effects may occur in the database: - Funds (either payments or credits) may get assigned to the invoice. - The invoice status may change. - Credits entities may be created. """ logger.info('assign-funds-to-invoice', invoice_id=invoice_id) invoice = Invoice.objects.get(pk=invoice_id) account_id = invoice.account_id # # Precondition. Don't touch invoices that are not PENDING # if invoice.status != Invoice.PENDING: logger.info('assign-funds-to-invoice.status-is-not-pending', invoice_id=invoice_id) return False # # Precondition: Only handle invoices in a single currency # invoice_due_monies = invoice.due().monies() if len(invoice_due_monies) != 1: logger.info('assign-funds-to-invoice.more-than-one-currency', invoice_id=invoice_id) return False invoice_due_amount = invoice_due_monies[0].amount invoice_due_currency = invoice_due_monies[0].currency # # 1. Collect funds as long as long as we need them # if invoice_due_amount > 0: payments = Transaction.successful \ .payments() \ .uninvoiced(account_id=account_id) \ .in_currency(invoice_due_currency) \ .order_by('created') credits = Charge.objects \ .credits() \ .uninvoiced(account_id=account_id) \ .in_currency(invoice_due_currency) \ .order_by('created') funds = list(credits) + list(payments) for fund in funds: contributed_amount = abs( fund.amount.amount ) # 'abs' because credits have a negative value logger.info('assign-funds-to-invoice.assigning-fund', invoice_id=invoice_id, fund_type=type(fund).__name__, fund_id=str(fund.pk), contributed_amount=contributed_amount) fund.invoice_id = invoice_id fund.save() invoice_due_amount -= contributed_amount if invoice_due_amount <= 0: break # # 2. Mark invoice paid if nothing is due. # if invoice_due_amount <= 0: logger.info('assign-funds-to-invoice.mark-paid', invoice_id=invoice_id, invoice_due_amount=invoice_due_amount) invoice.status = Invoice.PAID invoice.save() # # 3. Carry forward any overpaid money. # if invoice_due_amount < 0: overpayment = Money(abs(invoice_due_amount), invoice_due_currency) logger.info('assign-funds-to-invoice.handling-overpayment', invoice_id=invoice_id, overpayment=overpayment) with transaction.atomic(): Charge.objects.create(account_id=account_id, amount=overpayment, product_code=CARRIED_FORWARD, invoice_id=invoice_id) Charge.objects.create(account_id=account_id, amount=-overpayment, product_code=CREDIT_REMAINING) return invoice.status == Invoice.PAID
def setUp(self): user = User.objects.create_user('a-username') account = Account.objects.create(owner=user, currency='CHF') self.charge = Charge.objects.create(account=account, amount=Money(10, 'CHF'), product_code='ACHARGE')
def make_prices(): service = Service.objects.get(pk=4) country = Country.objects.get(pk=1) service.prices.all().delete() # SUCCESS: add base price price = Price() price.service = service price.price = Money(120, EUR) price.full_clean() price.save() # ERROR: add another base price with pytest.raises(ValidationError) as e: price.pk = None price.full_clean() price.save() assert 'Base price for this country already exists' in e.value.messages # SUCCESS: add base country price price.pk = None price.country = country price.price = Money(75, EUR) price.full_clean() price.save() # ERROR: add another base price with pytest.raises(ValidationError) as e: price.pk = None price.full_clean() price.save() assert 'Base price for this country already exists' in e.value.messages assert service.prices.all().count() == 2 # SUCCESS: add base period price 5-10 price.pk = None price.country = None price.price = Money(40, EUR) price.period_from = 5 price.period_to = 10 price.full_clean() price.save() # ERROR: add another period price 5-10 with pytest.raises(ValidationError) as e: price.pk = None price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # SUCCESS: add country period price 5-10 price.pk = None price.country = country price.price = Money(122, EUR) price.for_unit = False price.full_clean() price.save() # ERROR: add another period price 6-7 with pytest.raises(ValidationError) as e: price.pk = None price.country = None price.period_from = 6 price.period_to = 7 price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # ERROR: add another period price 7-14 with pytest.raises(ValidationError) as e: price.pk = None price.country = None price.period_from = 7 price.period_to = 14 price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # ERROR: add another period price 2-7 with pytest.raises(ValidationError) as e: price.pk = None price.country = None price.period_from = 2 price.period_to = 7 price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # ERROR: add another period price 10-12 with pytest.raises(ValidationError) as e: price.pk = None price.country = None price.period_from = 10 price.period_to = 12 price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # SUCCESS: add base period price 11-∞ price.pk = None price.price = Money(15, EUR) price.period_from = 11 price.for_unit = True price.period_to = None price.full_clean() price.save() # SUCCESS: add base period price ∞-4 price.pk = None price.price = Money(15, EUR) price.period_from = None price.period_to = 4 price.full_clean() price.save() # ERROR: add another period price 22-33 with pytest.raises(ValidationError) as e: price.pk = None price.country = None price.period_from = 22 price.period_to = 33 price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # ERROR: add another period price 1-3 with pytest.raises(ValidationError) as e: price.pk = None price.country = None price.period_from = 1 price.period_to = 3 price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # ERROR: add another country period price 5-11 with pytest.raises(ValidationError) as e: price.pk = None price.country = country price.period_from = 5 price.period_to = 11 price.full_clean() price.save() assert 'Price with this period already exists' in e.value.messages # SUCCESS: add another country period price 11-20 price.pk = None price.country = country price.price = Money(35, EUR) price.period_from = 11 price.period_to = 20 price.full_clean() price.save() price.pk = None price.country = Country.objects.get(pk=2) price.price = Money(2300, RUB) price.full_clean() price.save() assert service.prices.all().count() == 8
def test_uninvoiced_payments_should_ignore_invoiced_transactions(self): Invoice.objects.create(id=1, account=self.account, due_date=date.today()) Transaction.objects.create(account=self.account, success=True, invoice_id=1, amount=Money(10, 'CHF')) with self.assertNumQueries(1): qs = Transaction.successful.uninvoiced(account_id=self.account.pk).payments() assert not qs.exists()
def currency_exchange( source, source_amount, destination, destination_amount, trading_account, fee_destination=None, fee_amount=None, date=None, description=None, ): """Exchange funds from one currency to another Use this method to represent a real world currency transfer. Note this process doesn't care about exchange rates, only about the value of currency going in and out of the transaction. You can also record any exchange fees by syphoning off funds to ``fee_account`` of amount ``fee_amount``. Note that the free currency must be the same as the source currency. Examples: For example, imagine our Canadian bank has obligingly transferred 120 CAD into our US bank account. We sent CAD 120, and received USD 100. We were also changed 1.50 CAD in fees. We can represent this exchange in Hordak as follows:: from hordak.utilities.currency import currency_exchange currency_exchange( # Source account and amount source=cad_cash, source_amount=Money(120, 'CAD'), # Destination account and amount destination=usd_cash, destination_amount=Money(100, 'USD'), # Trading account the exchange will be done through trading_account=trading, # We also incur some fees fee_destination=banking_fees, fee_amount=Money(1.50, 'CAD') ) We should now find that: 1. ``cad_cash.balance()`` has decreased by ``CAD 120`` 2. ``usd_cash.balance()`` has increased by ``USD 100`` 3. ``banking_fees.balance()`` is ``CAD 1.50`` 4. ``trading_account.balance()`` is ``USD 100, CAD -120`` You can perform ``trading_account.normalise()`` to discover your unrealised gains/losses on currency traded through that account. Args: source (Account): The account the funds will be taken from source_amount (Money): A ``Money`` instance containing the inbound amount and currency. destination (Account): The account the funds will be placed into destination_amount (Money): A ``Money`` instance containing the outbound amount and currency trading_account (Account): The trading account to be used. The normalised balance of this account will indicate gains/losses you have made as part of your activity via this account. Note that the normalised balance fluctuates with the current exchange rate. fee_destination (Account): Your exchange may incur fees. Specifying this will move incurred fees into this account (optional). fee_amount (Money): The amount and currency of any incurred fees (optional). description (str): Description for the transaction. Will default to describing funds in/out & fees (optional). date (datetime.date): The date on which the transaction took place. Defaults to today (optional). Returns: (Transaction): The transaction created See Also: You can see the above example in practice in ``CurrencyExchangeTestCase.test_fees`` in `test_currency.py`_. .. _test_currency.py: https://github.com/adamcharnock/django-hordak/blob/master/hordak/tests/utilities/test_currency.py """ from hordak.models import Account, Leg, Transaction if trading_account.type != Account.TYPES.trading: raise TradingAccountRequiredError( "Account {} must be a trading account".format(trading_account)) if (fee_destination or fee_amount) and not (fee_destination and fee_amount): raise RuntimeError( "You must specify either neither or both fee_destination and fee_amount." ) if fee_amount is None: # If fees are not specified then set fee_amount to be zero fee_amount = Money(0, source_amount.currency) else: # If we do have fees then make sure the fee currency matches the source currency if fee_amount.currency != source_amount.currency: raise InvalidFeeCurrency( "Fee amount currency ({}) must match source amount currency ({})" .format(fee_amount.currency, source_amount.currency)) # Checks over and done now. Let's create the transaction with db_transaction.atomic(): transaction = Transaction.objects.create( date=date or datetime.date.today(), description=description or "Exchange of {} to {}, incurring {} fees".format( source_amount, destination_amount, "no" if fee_amount is None else fee_amount, ), ) # Source currency into trading account Leg.objects.create(transaction=transaction, account=source, amount=source_amount) Leg.objects.create( transaction=transaction, account=trading_account, amount=-(source_amount - fee_amount), ) # Any fees if fee_amount and fee_destination: Leg.objects.create( transaction=transaction, account=fee_destination, amount=-fee_amount, description="Fees", ) # Destination currency out of trading account Leg.objects.create(transaction=transaction, account=trading_account, amount=destination_amount) Leg.objects.create(transaction=transaction, account=destination, amount=-destination_amount) return transaction
def test_it_should_compute_the_invoice_due_in_multiple_currencies(self): invoice = Invoice.objects.create(account=self.account, due_date=date.today()) Charge.objects.create(account=self.account, invoice=invoice, amount=Money(10, 'CHF'), product_code='ACHARGE') Charge.objects.create(account=self.account, invoice=invoice, amount=Money(-3, 'EUR'), product_code='ACREDIT') with self.assertNumQueries(2): assert invoice.due() == Total(10, 'CHF', -3, 'EUR')
def search_and_match(self, manufacturer_part, quantity=1, currency=None): manufacturer = manufacturer_part.manufacturer manufacturer_part_number = manufacturer_part.manufacturer_part_number if manufacturer: manufacturer_list = self.api.get_manufacturer_list() # TODO: possibly get manufacturer id from manufacturer list, do a fuzzy lookup using manufacturer name # to reduce results mfg_id = manufacturer_list[ manufacturer. name] if manufacturer.name in manufacturer_list else None if mfg_id: results = self.api.search_part_and_manufacturer( part_number=manufacturer_part_number, manufacturer_id=mfg_id) else: results = self.api.search_part( part_number=manufacturer_part_number) else: results = self.api.search_part( part_number=manufacturer_part_number) mouser_parts = [] optimal_part = None seller_parts = [] for part in results['Parts']: seller = Seller(name='Mouser') try: quantity_available = [ int(s) for s in part['Availability'].split() if s.isdigit() ][0] mouser_part = { 'part_number': part['ManufacturerPartNumber'], 'manufacturer': part['Manufacturer'], 'description': part['Description'], 'data_sheet': part['DataSheetUrl'], 'stock': part['Availability'], 'stock_parsed': quantity_available, 'lead_time': part['LeadTime'], 'seller_parts': [], 'product_detail_url': part['ProductDetailUrl'], } lead_time_days = [ int(s) for s in part['LeadTime'].split() if s.isdigit() ][0] # TODO: Make sure it's actually days for pb in part['PriceBreaks']: moq = int(pb['Quantity']) unit_price_raw = parse_number(pb['Price']) unit_currency = pb['Currency'] unit_cost = Money(unit_price_raw, unit_currency) if currency: unit_cost = convert_money(unit_cost, currency) seller_part = SellerPart( seller=seller, manufacturer_part=manufacturer_part, minimum_order_quantity=moq, minimum_pack_quantity=1, data_source='Mouser', unit_cost=unit_cost, lead_time_days=lead_time_days, nre_cost=Money(0, currency), ncnr=True) mouser_part['seller_parts'].append(seller_part.as_dict()) seller_parts.append(seller_part) mouser_parts.append(mouser_part) except (KeyError, AttributeError, IndexError): continue local_seller_parts = list(manufacturer_part.seller_parts()) seller_parts.extend(local_seller_parts) return { 'mouser_parts': mouser_parts, 'optimal_seller_part': SellerPart.optimal(seller_parts, quantity), }
def to_internal_value(self, data): if isinstance(data, Money): amount = super(MoneyField, self).to_internal_value(data.amount) return Money(amount, data.currency) return super(MoneyField, self).to_internal_value(data)