Esempio n. 1
0
    def _process_subscription_charges(self, charges, subscription, family):
        for charge in charges:

            state = charge["state"]
            if state == "refunded":
                refund = True
            elif state == "captured":
                refund = False
            elif state == "failed":
                continue
            else:
                print("State not handled: " + state)
                continue

            sale = Sale()
            sale.payer_name = subscription['payer_name']
            sale.payer_email = subscription['payer_email']
            if subscription['fee_payer'] == "payer":
                print(
                    "Fee is paid by payer. Situation has not yet been analyzed."
                )
            sale.payment_method = Sale.PAID_BY_WEPAY
            sale.sale_date = date.fromtimestamp(int(
                charge['create_time']))  # TODO: This is UTC timezone.
            sale.total_paid_by_customer = charge["amount"]
            sale.processing_fee = charge["fee"]
            sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX,
                                         charge['subscription_charge_id'])
            django_sale = self.upsert(sale)

            mship = Membership()
            mship.sale = Sale(id=django_sale['id'])
            mship.sale_price = sale.total_paid_by_customer
            if subscription['fee_payer'] == 'payer':
                mship.sale_price -= sale.processing_fee
            if family > 0: mship.sale_price -= Decimal(10.00) * Decimal(family)
            mship.ctrlid = "{}:{}".format(self.CTRLID_PREFIX,
                                          charge['subscription_charge_id'])
            mship.start_date = sale.sale_date
            mship.end_date = mship.start_date + relativedelta(months=1,
                                                              days=-1)
            self.upsert(mship)

            for n in range(family):
                fam = Membership()
                fam.sale = mship.sale
                fam.sale_price = 10.00
                fam.membership_type = Membership.MT_FAMILY
                fam.start_date = mship.start_date
                fam.end_date = mship.end_date
                fam.ctrlid = "{}:{}:{}".format(self.CTRLID_PREFIX,
                                               mship.ctrlid, n)
                self.upsert(fam)
Esempio n. 2
0
    def _process_membership_item(self, sale, item, item_num, membership_type,
                                 dur_amt, dur_unit):
        month = self.month_in_str(item['name'])

        family = None
        for modifier in item["modifiers"]:
            lmod = modifier["name"].lower()
            if lmod == "just myself": family = 0
            elif lmod == "1 add'l family member": family = 1
            elif lmod == "2 add'l family members": family = 2
            elif lmod == "3 add'l family members": family = 3
            elif lmod == "4 add'l family members": family = 4
            elif lmod == "5 add'l family members": family = 5
            if membership_type == Membership.MT_WORKTRADE and month is None:
                month = self.month_in_str(lmod)

        if family is None:
            ctrlid = sale['ctrlid']

            # A special case because the 12 month membership item was set up incorrectly.
            if ctrlid == "SQ:kZxawM1NBAxvqr2vKU2ZnzMF" and item_num == 1:
                family = 0

            else:
                print("Couldn't determine family count for {}:{}".format(
                    sale['ctrlid'], item_num))
                family = 0

        quantity = int(float(item['quantity']))
        for n in range(1, quantity + 1):
            mship = Membership()
            mship.sale = Sale(id=sale['id'])
            mship.membership_type = membership_type
            mship.ctrlid = "{}:{}:{}".format(sale['ctrlid'], item_num, n)
            mship.start_date = parse(sale['sale_date']).date()
            if mship.membership_type == Membership.MT_WORKTRADE:
                mship.start_date = mship.start_date.replace(
                    day=1)  # WT always starts on the 1st.
                if month is not None:  # Hopefully, the buyer specified the month.
                    mship.start_date = mship.start_date.replace(month=month)
            mship.end_date = mship.start_date + relativedelta(**{
                dur_unit: dur_amt,
                "days": -1
            })
            mship.sale_price = Decimal(
                item['gross_sales_money']['amount']) / Decimal(
                    quantity * 100.0)
            mship.sale_price -= Decimal(10.00) * Decimal(family)
            self.upsert(mship)

            for f in range(family):
                fam = Membership()
                fam.sale = mship.sale
                fam.sale_price = 10
                fam.membership_type = Membership.MT_FAMILY
                fam.start_date = mship.start_date
                fam.end_date = mship.end_date
                fam.ctrlid = "{}:{}".format(mship.ctrlid, f)
                self.upsert(fam)
Esempio n. 3
0
 def _special_case_7cQ69ctaeYok1Ry3KOTFbyMF(self, sale):
     mship = Membership()
     mship.sale = Sale(id=sale['id'])
     mship.membership_type = Membership.MT_REGULAR
     mship.ctrlid = "{}:1:1".format(sale['ctrlid'])
     mship.start_date = date(2016, 4, 5)
     mship.end_date = date(2015, 4, 18)
     mship.sale_price = 25.00
     self.upsert(mship)
Esempio n. 4
0
 def _special_case_ixStxgstn56QI8jnJtcCtzMF(self, sale):
     mship = Membership()
     mship.sale = Sale(id=sale['id'])
     mship.membership_type = Membership.MT_REGULAR
     mship.ctrlid = "{}:1:1".format(sale['ctrlid'])
     mship.start_date = date(2014, 12, 12)
     mship.end_date = date(2015, 6, 11)
     mship.sale_price = 225.00
     self.upsert(mship)
Esempio n. 5
0
    def _process_giftcard_item(self, sale, item, item_num):

        quantity = int(float(item['quantity']))
        for n in range(1, quantity + 1):
            cardref = MembershipGiftCardReference()
            cardref.ctrlid = "{}:{}:{}".format(sale['ctrlid'], item_num, n)
            cardref.sale = Sale(id=sale['id'])
            cardref.sale_price = Decimal(
                item["net_sales_money"]["amount"]) / Decimal(quantity * 100.0)
            self.upsert(cardref)
Esempio n. 6
0
    def _process_donation(self, sale, checkout, earmark=None):

        don = MonetaryDonation()
        don.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, checkout['checkout_id'])
        don.sale = Sale(id=sale.id)
        don.amount = sale.total_paid_by_customer
        if checkout['fee_payer'] == 'payer': don.amount -= sale.processing_fee
        if earmark is not None:
            don.earmark = earmark
        self.upsert(don)
Esempio n. 7
0
    def _process_donation_item(self, sale, item, item_num):

        quantity = int(float(item['quantity']))
        for n in range(1, quantity + 1):
            don = MonetaryDonation()
            don.ctrlid = "{}:{}:{}".format(sale['ctrlid'], item_num, n)
            don.sale = Sale(id=sale['id'])
            don.amount = Decimal(
                item["gross_sales_money"]["amount"]) / Decimal(
                    quantity * 100.0)
            self.upsert(don)
Esempio n. 8
0
 def _special_case_0JFN0loJ0kcy8DXCvuDVwwMF(self, sale):
     # Verify: This was erroneously entered as a donation but was really a work-trade payment.
     mship = Membership()
     mship.sale = Sale(id=sale['id'])
     mship.member = Member(
         id=19
     )  # Lookup by name would be better but I don't want to have names in the code.
     mship.membership_type = Membership.MT_WORKTRADE
     mship.ctrlid = "{}:1:1".format(sale['ctrlid'])
     mship.start_date = date(2015, 12, 1)
     mship.end_date = date(2015, 12, 31)
     mship.sale_price = 10.00
     self.upsert(mship)
Esempio n. 9
0
    def _process_membership_sale(self, sale, checkout, months, family):

        mship = Membership()
        mship.sale = Sale(id=sale.id)
        mship.sale_price = sale.total_paid_by_customer
        if checkout['fee_payer'] == 'payer': mship.sale_price -= sale.processing_fee
        if family > 0: mship.sale_price -= Decimal(10.00) * Decimal(family)
        mship.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, checkout['checkout_id'])
        mship.start_date = sale.sale_date
        mship.end_date = mship.start_date + relativedelta(months=months, days=-1)
        self.upsert(mship)

        for n in range(family):
            fam = Membership()
            fam.sale            = mship.sale
            fam.sale_price      = 10.00
            fam.membership_type = Membership.MT_FAMILY
            fam.start_date      = mship.start_date
            fam.end_date        = mship.end_date
            fam.ctrlid          = "{}:{}:{}".format(self.CTRLID_PREFIX, mship.ctrlid, n)
            self.upsert(fam)
Esempio n. 10
0
    def _process_recurring_payment(self, agreement: sdk.BillingAgreement,
                                   transaction):
        when_datetime = parse(transaction["time_stamp"])  # type: datetime
        when_local_date = localtime(when_datetime).date()

        payer_name = transaction["payer_name"]
        payer_email = transaction["payer_email"]
        paid_amount = transaction["amount"]["value"]
        net_amount = transaction["net_amount"]["value"]
        fee_amount = str(
            Decimal(-1.0) * Decimal(transaction["fee_amount"]["value"]))
        trans_id = transaction["transaction_id"]
        fam_count = int((float(paid_amount) - 50.00) / 10.00)

        assert transaction["net_amount"]["currency"] == "USD"
        assert transaction["fee_amount"]["currency"] == "USD"
        assert transaction["amount"]["currency"] == "USD"
        assert transaction["time_zone"] == "GMT"
        assert float(paid_amount) >= 50.00

        sale = Sale()
        sale.payment_method = Sale.PAID_BY_PAYPAL
        sale.payer_email = payer_email
        sale.payer_name = payer_name
        sale.sale_date = when_local_date
        sale.total_paid_by_customer = Decimal(
            paid_amount
        )  # The full amount paid by the person, including payment processing fee IF CUSTOMER PAID IT.
        sale.processing_fee = Decimal(fee_amount)
        sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, trans_id)
        django_sale = self.upsert(sale)
        sale.id = django_sale['id']

        if django_sale["protected"]:
            # If the sale is protected then all details are also protected.
            # Otherwise there's no way to protect a deletion.
            return

        self._member_and_family(sale, 1)
Esempio n. 11
0
    def _process_other_item(self, sale, item, item_num):

        quantity = int(float(item['quantity']))

        if item['name'] not in self.OTHER_ITEM_TYPE_MAP:
            print("Fetcher does not map: " + item['name'])
            return

        mappedname = self.OTHER_ITEM_TYPE_MAP[item['name']]
        typepk = self._get_id(self.URLS[OtherItemType], {'name': mappedname})
        if typepk is None:
            print("Server does not have other item type: " + mappedname)
            return

        other = OtherItem()

        other.type = OtherItemType(id=typepk)
        other.sale = Sale(id=sale['id'])
        other.sale_price = Decimal(
            item["net_sales_money"]["amount"]) / Decimal(quantity * 100.0)
        other.qty_sold = int(float(item['quantity']))
        other.ctrlid = "{}:{}".format(sale['ctrlid'], item_num)

        self.upsert(other)
Esempio n. 12
0
    def _process_checkouts(self, checkouts):
        assert len(checkouts) < self.limit
        for checkout in checkouts:

            # Filter out questionable checkouts

            if not checkout['state'].startswith("captured"):
                continue

            # At this point we know we'll be creating an IncomeTransaction (currently called Sale)

            sale = Sale()
            sale.payment_method = Sale.PAID_BY_WEPAY
            sale.payer_email = checkout['payer_email']
            sale.payer_name = checkout['payer_name']
            sale.sale_date = date.fromtimestamp(int(
                checkout['create_time']))  # TODO: This is UTC timezone.
            sale.total_paid_by_customer = Decimal(
                checkout['gross']
            )  # Docs: "The total dollar amount paid by the payer"
            sale.processing_fee = Decimal(checkout['fee']) + Decimal(
                checkout['app_fee'])
            if checkout['fee_payer'] == 'payer':
                sale.fee_payer = Sale.FEE_PAID_BY_CUSTOMER
            else:
                sale.fee_payer = Sale.FEE_PAID_BY_US
            sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX,
                                         checkout['checkout_id'])
            django_sale = self.upsert(sale)
            sale.id = django_sale['id']

            desc = checkout['short_description']

            if checkout['checkout_id'] in [1877931854, 390559320]:
                # These are membership purchases that were erroneously entered as donations.
                self._process_membership_sale(sale, checkout, 6, 0)
                continue

            if checkout['amount'] == 20 \
             and desc == "Recurring Payment to Donation" \
             and md5(checkout['payer_name'].encode('utf-8')).hexdigest() == "95c53a5e254c1847ad8526b625862294":
                # Dale says that this recurring donation should be treated as a grandfathered membership.
                # I'm checking against md5 of the payer-name so I don't have personal info in source.
                self._process_membership_sale(sale, checkout, 1, 0)
                continue

            months = None
            if desc.startswith("One Month Membership"): months = 1
            elif desc.startswith("Three Month Membership"): months = 3
            elif desc.startswith("Six Month Membership"): months = 6
            elif desc.startswith("Recurring Payment to Dues-Paying Member"):
                months = 1
            elif desc.startswith("Payment to Dues-Paying Member ONE-TIME"):
                months = 1
            if months is not None:
                if desc.endswith("+ 1 family member"): family = 1
                elif desc.endswith("+ 2 family member"): family = 2
                elif desc.endswith("+ 3 family member"): family = 3
                elif desc.endswith("+ 4 family member"): family = 4
                elif desc.endswith("+ 5 family member"): family = 5
                elif desc.endswith("+ 6 family member"): family = 6
                else: family = 0
                self._process_membership_sale(sale, checkout, months, family)
                continue

            if desc in [
                    "Xerocraft KMKR Radio Donation",
                    "Single Maketopolis Ticket Purchase"
            ]:
                # The Maketopolis tickets were for KMKR's musical portion of the event.
                self._process_donation(sale, checkout, ACCT_KMKR_CAMPAIGN)
                continue

            if desc.endswith("Event Payment") \
             or desc.startswith("Recurring Payment to Donation") \
             or desc.startswith("Payment to Donation at"):
                self._process_donation(sale, checkout)
                continue

            print("Didn't recognize: " + desc)
Esempio n. 13
0
    def _process_payments(self, payments):

        for payment in payments:

            # TODO: Clean out any existing sale & line items in case of refund, and then skip.
            # Refund sensing logic will be something like this:
            #    if len(payment.refunds)>0
            #      and payment.refunds[0].payment_id == payment.id
            #      and payment.refunds[0].type == "FULL"

            if payment['tender'][0]['type'] == "NO_SALE":
                continue

            if payment['id'] in self.SALES_TO_SKIP: continue

            if len(payment["tender"]) != 1:
                print(
                    "Code doesn't handle multiple tenders as in {}. Skipping.".
                    format(payment['id']))
                continue

            sale = Sale()
            sale.sale_date = parse(payment["created_at"]).date()
            sale.payer_name = self.get_name_from_receipt(
                payment['receipt_url'])
            sale.payer_email = ""  # Annoyingly, not provided by Square.
            sale.payment_method = Sale.PAID_BY_SQUARE
            sale.method_detail = self._get_tender_type(payment)
            sale.total_paid_by_customer = Decimal(
                payment["tender"][0]["total_money"]["amount"]) / Decimal(100)
            sale.processing_fee = abs(
                Decimal(
                    payment["processing_fee_money"]["amount"])) / Decimal(100)
            sale.ctrlid = "SQ:" + payment["id"]
            #sale.payer_notes = payment.get("notes", "").strip()

            if payment['id'] == "ixStxgstn56QI8jnJtcCtzMF":
                sale.payer_name = sale.payer_name.replace("M ", "MIKE ")

            django_sale = self.upsert(sale)

            if payment['id'] == "7cQ69ctaeYok1Ry3KOTFbyMF":
                # Person wanted to pay for two weeks while he was in town.
                # I added an item for this but don't like the way it worked out.
                # So I'm treating this as a special case.
                self._special_case_7cQ69ctaeYok1Ry3KOTFbyMF(django_sale)

            elif payment['id'] == "0JFN0loJ0kcy8DXCvuDVwwMF":
                # This is a work trade payment that was erroneously entered as a custom payment.
                # So we do this special processing to ingest it as a 1 month Work Trade membership.
                self._special_case_0JFN0loJ0kcy8DXCvuDVwwMF(django_sale)

            elif payment['id'] == "ixStxgstn56QI8jnJtcCtzMF":
                # This is a six month membership that was erroneously entered as a custom payment.
                # So we do this special processing to ingest it as a 6 month membership.
                self._special_case_ixStxgstn56QI8jnJtcCtzMF(django_sale)

            else:
                if django_sale["protected"] == True:
                    # If the sale is protected then all details are also protected.
                    # Otherwise there's no way to protect a deletion.
                    return
                itemizations = payment['itemizations']
                self._process_itemizations(itemizations, django_sale)
Esempio n. 14
0
    def _process_payment(self, payment: sdk.Payment):
        payment_id = payment['id']
        if payment_id in self.PAYMENTS_TO_IGNORE:
            return
        state = payment['state']  # e.g. 'approved'
        when_datetime = parse(payment['create_time'])  # type: datetime
        when_local_date = localtime(when_datetime).date()
        if 'payer' not in payment:
            print(payment)
            return
        if 'payer_info' not in payment['payer']:
            print(payment)
            return
        who_fname = payment['payer']['payer_info']['first_name']
        who_lname = payment['payer']['payer_info']['last_name']
        try:
            who_email = payment['payer']['payer_info']['email']
        except:
            who_email = ""

        assert len(
            payment['transactions']
        ) == 1, "Code can't yet deal with multiple transactions payment."
        transaction = payment['transactions'][0]
        payment_amount = transaction['amount']['total']
        if 'custom' in transaction:  # Xerocraft.org puts an indication of what was paid for in "custom"
            what = transaction['custom']
        else:
            what = None

        resources = transaction['related_resources']
        sale_amt = None
        refund_amt = None
        if len(resources) == 0:
            # This has only been observed in a test transaction generated by Kyle
            print("K", end="")
            return
        for resource in resources:
            if 'sale' in resource:
                sale = resource['sale']
                if sale_amt is not None:
                    print(
                        "WARNING (not handled): Multiple sale resources in sale to "
                        + who_email + " on " + str(when_local_date))
                sale_amt = float(sale['amount']['total'])
                pay_mode = sale['payment_mode']  # e.g. 'INSTANT_TRANSFER'
                trans_fee = float(sale['transaction_fee']['value'])
            if 'refund' in resource:
                refund = resource['refund']
                if refund['state'] is "failed":
                    continue
                if refund_amt is not None:
                    print(
                        "WARNING (not handled): Multiple refunds in sale to " +
                        who_email + " on " + str(when_local_date))
                refund_amt = float(refund['amount']['total'])
            if sale_amt is not None and refund_amt is not None:
                # NOTE: Refunds will require manual processing. Adjusted transactions should be marked "protected".
                if sale_amt == refund_amt:
                    print("R", end="")
                    return
                else:
                    print(
                        "Code doesn't yet deal with partial refunds. Sale was to "
                        + who_email + " on " + str(when_local_date))
                    return

        sale = Sale()
        sale.payment_method = Sale.PAID_BY_PAYPAL
        sale.payer_email = who_email
        sale.payer_name = "{} {}".format(who_fname, who_lname)
        sale.sale_date = when_local_date
        sale.total_paid_by_customer = Decimal(
            sale_amt
        )  # The full amount paid by the person, including payment processing fee IF CUSTOMER PAID IT.
        sale.processing_fee = Decimal(trans_fee)
        sale.ctrlid = "{}:{}".format(self.CTRLID_PREFIX, payment_id)
        django_sale = self.upsert(sale)
        sale.id = django_sale['id']

        if django_sale["protected"] == True:
            # If the sale is protected then all details are also protected.
            # Otherwise there's no way to protect a deletion.
            return

        if what is None:
            self._process_unknown_item(sale)
        elif what.startswith("DON_"):
            self._process_donation_item(sale, what)
        elif what.startswith("MSHIP_"):
            self._process_non_recurring_membership_item(sale, what)
        else:
            print("Unkown item: " + what)