diff --git a/src/pages/automatic/routes.ts b/src/pages/automatic/routes.ts index 9af6b82..c65b10a 100755 --- a/src/pages/automatic/routes.ts +++ b/src/pages/automatic/routes.ts @@ -1,8 +1,7 @@ import AutomaticHome from './home.vue'; - - +import AutomaticView from './view.vue'; const autoRoutes = [ { @@ -10,6 +9,11 @@ const autoRoutes = [ name: 'auto', component: AutomaticHome, }, + { + path: '/automatic/:id', + name: 'automaticView', + component: AutomaticView, + }, ] export default autoRoutes diff --git a/src/pages/automatic/view.vue b/src/pages/automatic/view.vue new file mode 100644 index 0000000..3b7a04f --- /dev/null +++ b/src/pages/automatic/view.vue @@ -0,0 +1,448 @@ + + + + + diff --git a/src/pages/customer/profile/profile/AutomaticDeliveries.vue b/src/pages/customer/profile/profile/AutomaticDeliveries.vue index 7e3f7be..b3b5e4d 100644 --- a/src/pages/customer/profile/profile/AutomaticDeliveries.vue +++ b/src/pages/customer/profile/profile/AutomaticDeliveries.vue @@ -12,6 +12,7 @@ Name Gallons Date + View @@ -20,6 +21,11 @@ {{ auto.customer_full_name }} {{ auto.gallons_delivered }} {{ auto.fill_date }} + + + + + @@ -44,4 +50,4 @@ interface Props { // 3. Use the typed defineProps defineProps(); - \ No newline at end of file + diff --git a/src/pages/customer/profile/profile/TransactionsTable.vue b/src/pages/customer/profile/profile/TransactionsTable.vue index edaa049..7d8043b 100644 --- a/src/pages/customer/profile/profile/TransactionsTable.vue +++ b/src/pages/customer/profile/profile/TransactionsTable.vue @@ -114,6 +114,7 @@ interface Transaction { rejection_reason: string | null; delivery_id: number | null; service_id: number | null; + auto_id: number | null; } interface Props { @@ -125,7 +126,9 @@ defineProps(); const getStatusClass = (status: number) => status === 0 ? 'badge-success' : 'badge-error'; const getStatusText = (status: number) => status === 0 ? 'Approved' : 'Declined'; const getSourceText = (transaction: Transaction) => { - if (transaction.delivery_id) { + if (transaction.auto_id) { + return 'Automatic'; + } else if (transaction.delivery_id) { return 'Delivery - ' + transaction.delivery_id; } else if (transaction.service_id) { return 'Service - ' + transaction.service_id; diff --git a/src/pages/delivery/update_tickets/finalize_ticket_auto.vue b/src/pages/delivery/update_tickets/finalize_ticket_auto.vue index 0a9352e..1be5276 100644 --- a/src/pages/delivery/update_tickets/finalize_ticket_auto.vue +++ b/src/pages/delivery/update_tickets/finalize_ticket_auto.vue @@ -103,7 +103,7 @@
- +
@@ -497,6 +497,19 @@ }, + closeTicket(ticketId: number) { + let path = import.meta.env.VITE_AUTO_URL + "/confirm/auto/close_ticket/" + ticketId; + axios({ + method: "put", + url: path, + withCredentials: true, + headers: authHeader(), + }) + .then(() => { + // Ticket closed successfully + }) + }, + UpdateDeliveredAuto(payload: { gallons_delivered: string, }) { @@ -516,7 +529,7 @@ text: "Auto Updated", type: "success", }); - this.$router.push({ name: "payAutoCapture", params: { id: this.autoTicket.id } }); + // Removed redirect from here, will handle in onSubmit } }) }, @@ -554,12 +567,20 @@ }) }, onSubmit() { - let payload = { gallons_delivered: this.FinalizeOilOrderForm.gallons_delivered, }; this.UpdateDeliveredAuto(payload); - + if (this.autoTicket.payment_status == '1') { + // Pre-authorized: redirect to capture page + this.$router.push({ name: "payAutoCapture", params: { id: this.autoTicket.id } }); + } else { + // Fully charged: close ticket + if (this.autoDelivery.open_ticket_id) { + this.closeTicket(this.autoDelivery.open_ticket_id); + } + this.$router.push({ name: "auto" }); + } }, }, diff --git a/src/pages/pay/auto/authorize_precharge_autho.vue b/src/pages/pay/auto/authorize_precharge_autho.vue index 74bef2c..9abe235 100644 --- a/src/pages/pay/auto/authorize_precharge_autho.vue +++ b/src/pages/pay/auto/authorize_precharge_autho.vue @@ -19,48 +19,94 @@

Payment Authorization Authorize.net

- +
- -
-

Charge Breakdown

-
-
- Gallons to Fill: - {{ calculateGallonsToFill() }} gallons + +
+ +
+
+
+
{{ customer.customer_first_name }} {{ customer.customer_last_name }}
+
Account: {{ customer.account_number }}
+
+ + View Profile +
-
- Price per Gallon: - ${{ pricing.price_for_customer || currentOilPrice }} -
-
- Subtotal: - ${{ calculateSubtotal() }} -
-
-
- Estimated Total: - ${{ calculateTotalAmount() }} -
-
-
- - -
-

Payment Method

-
-
-
{{ selectedCard.type_of_card }}
-
Primary
-
-
-
{{ selectedCard.card_number }}
-
{{ selectedCard.name_on_card }}
-
Expires: {{ selectedCard.expiration_month }}/{{ selectedCard.expiration_year }}
+
+
{{ customer.customer_address }}
+
{{ customer.customer_apt }}
+
{{ customer.customer_town }}, {{ customerStateName }} {{ customer.customer_zip }}
+
{{ customer.customer_phone_number }}
-
- No payment method selected + + +
+

Payment Method

+
+
+
{{ selectedCard.type_of_card }}
+
Primary
+
+
+
{{ selectedCard.card_number }}
+
{{ selectedCard.name_on_card }}
+
Expires: {{ selectedCard.expiration_month }}/{{ selectedCard.expiration_year }}
+
+
+
+ No payment method selected +
+
+
+ + +
+ +
+

Charge Breakdown

+
+
+ Gallons to Fill: + {{ calculateGallonsToFill() }} gallons +
+
+ Price per Gallon: + ${{ pricing.price_for_customer || currentOilPrice }} +
+
+ Subtotal: + ${{ calculateSubtotal() }} +
+
+
+ Estimated Total: + ${{ calculateTotalAmount() }} +
+
+
+ + +
+

Today's Price Per Gallon

+
+ + + + + + + + + + + + + +
GallonsTotal Price
{{ tier.gallons }}${{ tier.price.toFixed(2) }}
+
@@ -92,6 +138,14 @@ :disabled="loading" />
+
+ +
@@ -162,6 +216,7 @@ export default defineComponent({ deliveryId: this.$route.params.id as string, loaded: false, chargeAmount: 0, + quickGallonAmounts: [100, 125, 150, 175, 200, 220], loading: false, action: '', // 'preauthorize' or 'charge' error: '', @@ -222,17 +277,26 @@ export default defineComponent({ date: "", }, currentOilPrice: 0, + pricingTiers: [] as { gallons: number; price: number }[], } }, computed: { selectedCard(): any { return this.credit_cards.find((card: any) => card.main_card) || this.credit_cards[0] + }, + customerStateName(): string { + const states: Record = { 0: 'Massachusetts', 1: 'Rhode Island', 2: 'New Hampshire', 3: 'Maine', 4: 'Vermont', 5: 'Connecticut', 6: 'New York' }; + return states[this.customer.customer_state] || 'Unknown state'; + }, + selectedGallonAmount(): number | null { + return this.quickGallonAmounts.find(gal => this.calculatePriceForGallons(gal).toFixed(2) === this.chargeAmount.toFixed(2)) || null; } }, mounted() { this.loadData(this.deliveryId) + this.getPricingTiers() }, created() { @@ -376,6 +440,55 @@ export default defineComponent({ }) }, + getPricingTiers() { + let path = import.meta.env.VITE_BASE_URL + "/info/price/oil/tiers"; + axios({ + method: "get", + url: path, + withCredentials: true, + headers: authHeader() + }) + .then((response: any) => { + this.pricingTiers = Object.entries(response.data).map(([gallons, price]) => ({ gallons: parseInt(gallons, 10), price: Number(price) })); + }) + .catch(() => { + notify({ + title: "Pricing Error", + text: "Could not retrieve today's pricing.", + type: "error" + }); + }); + }, + + isPricingTierSelected(tierGallons: number | string): boolean { + const calculated = this.calculateGallonsToFill() + return calculated == Number(tierGallons) + }, + + calculatePriceForGallons(gallons: number): number { + let priceForGallons = 0; + const sortedTiers = [...this.pricingTiers].sort((a, b) => Number(a.gallons) - Number(b.gallons)); + + // Find the highest tier that is less than or equal to the gallons ordered + let applicableTier = sortedTiers.filter(t => gallons >= Number(t.gallons)).pop(); + + if (applicableTier) { + const pricePerGallon = Number(applicableTier.price) / Number(applicableTier.gallons); + priceForGallons = gallons * pricePerGallon; + } else if (sortedTiers.length > 0) { + // Fallback to the lowest tier's price/gallon if no tier is met (e.g., ordering 50 gallons when lowest tier is 100) + const lowestTier = sortedTiers[0]; + const pricePerGallon = Number(lowestTier.price) / Number(lowestTier.gallons); + priceForGallons = gallons * pricePerGallon; + } + + return priceForGallons; + }, + + setGallons(gallons: number) { + this.chargeAmount = this.calculatePriceForGallons(gallons); + }, + calculateGallonsToFill() { return this.autoDelivery.tank_size - this.autoDelivery.estimated_gallons_left }, @@ -514,8 +627,8 @@ export default defineComponent({ // Create Tickets_Auto_Delivery after successful charge const ticketPayload = { - gallons_delivered: this.calculateGallonsToFill(), - payment_type: 1, // 1 for charge, 11 for preauthorize + gallons_delivered: 0, + payment_type: 11, // 11 for Authorize charge payment_card_id: this.selectedCard.id, payment_status: response.data.status // 0 = APPROVED }; diff --git a/src/pages/pay/auto/capture_authorize_autho.vue b/src/pages/pay/auto/capture_authorize_autho.vue index f8071f4..205b4cb 100644 --- a/src/pages/pay/auto/capture_authorize_autho.vue +++ b/src/pages/pay/auto/capture_authorize_autho.vue @@ -217,6 +217,24 @@
+ + + diff --git a/src/pages/pay/service/capture_authorize.vue b/src/pages/pay/service/capture_authorize.vue index bee7125..0e04266 100644 --- a/src/pages/pay/service/capture_authorize.vue +++ b/src/pages/pay/service/capture_authorize.vue @@ -183,6 +183,24 @@
+ + +