Skip to content

Commit

Permalink
Merge pull request #4167 from magfest/receipt-fixes
Browse files Browse the repository at this point in the history
Implement various fixes for receipts
  • Loading branch information
kitsuta committed Jul 7, 2023
2 parents f5fb779 + 53b0561 commit 3e33517
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 52 deletions.
2 changes: 1 addition & 1 deletion uber/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,7 @@ def preprocess_refund(self, txn, amount=0, already_refunded_error=True):
if not charge_id:
return "We could not find record of this payment being completed."

already_refunded = txn.update_amount_refunded()
already_refunded, last_refund_id = txn.update_amount_refunded()
if txn.amount - already_refunded <= 0:
if already_refunded_error:
return "This payment has already been fully refunded."
Expand Down
5 changes: 4 additions & 1 deletion uber/models/commerce.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,16 +341,19 @@ def update_amount_refunded(self):
if not self.intent_id:
return 0

last_refund_id = None
refunded_total = 0
for refund in stripe.Refund.list(payment_intent=self.intent_id):
refunded_total += refund.amount
last_refund_id = refund.id
self.refund_id = self.refund_id or last_refund_id
with Session() as session:
other_txns = session.query(ReceiptTransaction).filter_by(intent_id=self.intent_id
).filter(ReceiptTransaction.id != self.id)
other_refunds = sum([txn.refunded for txn in other_txns])

self.refunded = min(self.amount, refunded_total - other_refunds)
return self.refunded
return self.refunded, last_refund_id

@property
def cannot_delete_reason(self):
Expand Down
40 changes: 21 additions & 19 deletions uber/receipt_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@cost_calculation.MarketplaceApplication
def app_cost(app):
if app.status == c.APPROVED:
return ("Marketplace Application Fee", app.overridden_price * 100 or c.MARKETPLACE_FEE * 100 or 0)
return ("Marketplace Application Fee", app.overridden_price * 100 or c.MARKETPLACE_FEE * 100 or 0, None)


ArtShowApplication.cost_changes = {
Expand All @@ -28,27 +28,27 @@ def app_cost(app):
@cost_calculation.ArtShowApplication
def overridden_app_cost(app):
if app.status == c.APPROVED and app.overridden_price != None:
return ("Art Show Application (Custom Price)", app.overridden_price * 100)
return ("Art Show Application (Custom Price)", app.overridden_price * 100, 'overridden_price')

@cost_calculation.ArtShowApplication
def panel_cost(app):
return ("General Panel", c.COST_PER_PANEL * 100, app.panels) if app.panels else None
return ("General Panel", c.COST_PER_PANEL * 100, app.panels, None) if app.panels else None

@cost_calculation.ArtShowApplication
def table_cost(app):
return ("General Table", c.COST_PER_TABLE * 100, app.tables) if app.tables else None
return ("General Table", c.COST_PER_TABLE * 100, app.tables, None) if app.tables else None

@cost_calculation.ArtShowApplication
def mature_panel_cost(app):
return ("Mature Panel", c.COST_PER_PANEL * 100, app.panels_ad) if app.panels_ad else None
return ("Mature Panel", c.COST_PER_PANEL * 100, app.panels_ad, None) if app.panels_ad else None

@cost_calculation.ArtShowApplication
def mature_table_cost(app):
return ("Mature Table", c.COST_PER_TABLE * 100, app.tables_ad) if app.tables_ad else None
return ("Mature Table", c.COST_PER_TABLE * 100, app.tables_ad, None) if app.tables_ad else None

@cost_calculation.ArtShowApplication
def mailing_fee_cost(app):
return ("Mailing fee", c.ART_MAILING_FEE * 100) if app.delivery_method == c.BY_MAIL else None
return ("Mailing fee", c.ART_MAILING_FEE * 100, None) if app.delivery_method == c.BY_MAIL else None


Attendee.cost_changes = {
Expand Down Expand Up @@ -77,25 +77,27 @@ def badge_cost(attendee):
else:
label = "{} badge for {}".format(attendee.badge_type_label, attendee.full_name)

return (label, cost)
return (label, cost, None)

@cost_calculation.Attendee
def badge_upgrade_cost(attendee):
if attendee.badge_type in c.BADGE_TYPE_PRICES:
return ("{} badge upgrade for {}".format(attendee.badge_type_label, attendee.full_name), attendee.calculate_badge_prices_cost() * 100)
return ("{} badge upgrade for {}".format(attendee.badge_type_label, attendee.full_name),
attendee.calculate_badge_prices_cost() * 100, 'badge_type')

@cost_calculation.Attendee
def shipping_fee_cost(attendee):
if attendee.badge_status == c.DEFERRED_STATUS and attendee.amount_extra:
return ("Merch Shipping Fee", attendee.calculate_shipping_fee_cost() * 100)
return ("Merch Shipping Fee", attendee.calculate_shipping_fee_cost() * 100, None)

@cost_calculation.Attendee
def donation_cost(attendee):
return ("Extra Donation", attendee.extra_donation * 100) if attendee.extra_donation else None
return ("Extra Donation", attendee.extra_donation * 100, 'extra_donation') if attendee.extra_donation else None

@cost_calculation.Attendee
def kickin_cost(attendee):
return ("Kickin ({})".format(attendee.amount_extra_label), attendee.amount_extra * 100) if attendee.amount_extra else None
return ("Kickin ({})".format(attendee.amount_extra_label),
attendee.amount_extra * 100, 'amount_extra') if attendee.amount_extra else None

@credit_calculation.Attendee
def age_discount(attendee):
Expand All @@ -105,13 +107,13 @@ def age_discount(attendee):
else:
age_discount = attendee.age_discount * 100

return ("Age Discount", age_discount)
return ("Age Discount", age_discount, None)

@credit_calculation.Attendee
def group_discount(attendee):
if c.GROUP_DISCOUNT and attendee.qualifies_for_discounts and not attendee.age_discount and (
attendee.promo_code_groups or attendee.group):
return ("Group Discount", c.GROUP_DISCOUNT * 100 * -1)
return ("Group Discount", c.GROUP_DISCOUNT * 100 * -1, None)


Group.cost_changes = {
Expand All @@ -124,25 +126,25 @@ def group_discount(attendee):
def table_cost(group):
table_count = int(float(group.tables))
if table_count and group.auto_recalc:
return ("{} Tables".format(table_count), sum(c.TABLE_PRICES[i] for i in range(1, 1 + table_count)) * 100)
return ("{} Tables".format(table_count), sum(c.TABLE_PRICES[i] for i in range(1, 1 + table_count)) * 100, None)

@cost_calculation.Group
def badge_cost(group):
cost_table = defaultdict(int)

if not group.auto_recalc:
return None
return

for attendee in group.attendees:
if attendee.paid == c.PAID_BY_GROUP and attendee.badge_cost:
cost_table[attendee.badge_cost * 100] += 1

return ("Group badge ({})".format(group.name), cost_table)
return ("Group badge ({})".format(group.name), cost_table, None)

@cost_calculation.Group
def set_cost(group):
if not group.auto_recalc:
return ("Custom fee for group {}".format(group.name), group.cost * 100)
return ("Custom fee for group {}".format(group.name), group.cost * 100, None)


@cost_calculation.Attendee
Expand All @@ -159,4 +161,4 @@ def promo_code_group_cost(attendee):
return

return ("Group badge ({})".format(attendee.promo_code_groups[0].name if attendee.promo_code_groups
else getattr(attendee, 'name', 'Unknown')), cost_table)
else getattr(attendee, 'name', 'Unknown')), cost_table, None)
12 changes: 10 additions & 2 deletions uber/site_sections/reg_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,17 @@ def refresh_receipt_txn(self, session, id='', **params):

if txn.amount_left > 0:
prior_amount = txn.amount - txn.amount_left
new_amount = txn.update_amount_refunded()
if prior_amount != new_amount:
new_amount, last_refund_id = txn.update_amount_refunded()
if prior_amount < new_amount:
messages.append("Refund amount updated from {} to {}.".format(format_currency(prior_amount / 100),
format_currency(new_amount / 100)))
session.add(ReceiptTransaction(
receipt_id=txn.receipt_id,
refund_id=last_refund_id,
amount=(new_amount - prior_amount) * -1,
desc="Automatic refund of Stripe transaction " + txn.stripe_id,
who=AdminAccount.admin_name() or 'non-admin'
))

session.commit()
else:
Expand All @@ -351,6 +358,7 @@ def refresh_receipt_txn(self, session, id='', **params):
@ajax
def refund_receipt_txn(self, session, id='', **params):
txn = session.receipt_transaction(id)
return {'error': float(params.get('amount', 0)) * 100}

error = session.refund_item_or_txn(amount=float(params.get('amount', 0)) * 100, txn=txn)
if error:
Expand Down
53 changes: 31 additions & 22 deletions uber/templates/reg_admin/receipt_items.html
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,12 @@
callback: function (result) {
$("#refundTxnDiv").html(form);
if(result) {
$.post('refund_receipt_txn', {csrf_token: csrf_token, id: txnId, amount: $("#refundTxnForm [name='amount']").val() || 0}, function (response) {
$.post('refund_receipt_txn', {csrf_token: csrf_token, id: txnId, amount: refundAmt}, function (response) {
toastr.clear();
if (response.error) {
toastr.error(response.error);
} else {
toastr.info('Transaction refunded');
toastr.info(response.message);
updateRow(response.refunded, ' td:last #amt-refunded', " ($" + (response.refund_total / 100).toFixed(2) + " refunded)");
updateTotal(response.new_total, response.disable_button);
}
Expand All @@ -205,6 +205,17 @@
});
};

var compOrUndoRefundItem = function (page_handler, itemId, amount) {
$.post(page_handler, {csrf_token: csrf_token, id: itemId, amount: amount}, function (response) {
toastr.clear();
if (response.error) {
toastr.error(response.error);
} else {
window.location = 'receipt_items?id={{ model.id }}&message=' + response.message;
}
}, 'json');
}

var refundItem = function (itemId, amount, count, canRevert) {
var bootboxBtns = {
cancel: {
Expand All @@ -214,17 +225,10 @@
comp: {
label: 'Comp and Refund',
className: 'btn-success',
callback: function () {
$.post('comp_refund_receipt_item', {csrf_token: csrf_token, id: itemId, amount: amount.substring(1)}, function (response) {
toastr.clear();
if (response.error) {
toastr.error(response.error);
} else {
toastr.info('Transaction refunded');
updateRow(response.refunded, ' td:last #amt-refunded', " ($" + (response.refund_total / 100).toFixed(2) + " refunded)");
updateTotal(response.new_total, response.disable_button);
}
}, 'json');
callback: function (result) {
if(result) {
compOrUndoRefundItem('comp_refund_receipt_item', itemId, amount.substring(1))
}
}
}
}
Expand All @@ -233,6 +237,11 @@
bootboxBtns.revert = {
label: 'Undo and Refund',
className: 'btn-danger',
callback: function (result) {
if(result) {
compOrUndoRefundItem('undo_refund_receipt_item', itemId, amount.substring(1))
}
}
}
}

Expand Down Expand Up @@ -478,16 +487,16 @@ <h2>Receipt{% if other_receipts %}s{% endif %} for {% if model.attendee %}{{ mod
{% endif %}
</td>
<td>
{% if item.refundable %}
{% if item.refundable and not item.method %} {# disabling transaction refunds for now #}
<button class="btn btn-sm btn-warning"
onClick="
{% if item.method %}
refundTxn('{{ item.id }}', '{{ (item.amount_left / 100)|format_currency }}')
{% else %}
refundItem('{{ item.id }}', '{{ (item.amount / 100)|format_currency }}', '{{ item.count }}', '{{ item.revert_change|yesno }}')
{% endif %}"
>
Refund
onClick="
{% if item.method %}
refundTxn('{{ item.id }}', '{{ (item.amount_left / 100)|format_currency }}')">
Refund
{% else %}
refundItem('{{ item.id }}', '{{ (item.amount / 100)|format_currency }}', '{{ item.count }}', '{{ item.revert_change|yesno }}')">
Refund Item
{% endif %}
</button>
{% endif %}
</td>
Expand Down
18 changes: 11 additions & 7 deletions uber/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1085,11 +1085,13 @@ def create_new_receipt(cls, model, create_model=False, items=None):
item = calculation(model)
if item:
try:
desc, cost, count = item
desc, cost, col_name, count = item
except ValueError:
# Unpack list of wrong size (no quantity provided).
desc, cost = item
desc, cost, col_name = item
count = 1

default_val = getattr(model.__class__(), col_name, None) if col_name else None
if isinstance(cost, Iterable):
# A list of the same item at different prices, e.g., group badges
for price in cost:
Expand All @@ -1098,16 +1100,18 @@ def create_new_receipt(cls, model, create_model=False, items=None):
desc=desc,
amount=price,
count=cost[price],
who=AdminAccount.admin_name() or 'non-admin'
who=AdminAccount.admin_name() or 'non-admin',
revert_change={col_name: default_val} if col_name else {}
))
else:
receipt_items.append((desc, price, cost[price]))
elif receipt:
receipt_items.append(ReceiptItem(receipt_id=receipt.id,
desc=desc,
amount=cost,
count=count,
who=AdminAccount.admin_name() or 'non-admin'
desc=desc,
amount=cost,
count=count,
who=AdminAccount.admin_name() or 'non-admin',
revert_change={col_name: default_val} if col_name else {}
))
else:
receipt_items.append((desc, cost, count))
Expand Down

0 comments on commit 3e33517

Please sign in to comment.