add interest
This commit is contained in:
@@ -19,6 +19,7 @@ from pydantic import (
|
||||
from api_gateway import JSONResponse
|
||||
from boto3clients import dynamodb_client
|
||||
from config import ORDER_TABLE
|
||||
from routes.enrollments.enroll import Enrollment
|
||||
|
||||
router = Router()
|
||||
dyn = DynamoDBPersistenceLayer(ORDER_TABLE, dynamodb_client)
|
||||
@@ -66,6 +67,7 @@ class Checkout(BaseModel):
|
||||
address: Address
|
||||
payment_method: Literal['PIX', 'CREDIT_CARD', 'BANK_SLIP', 'MANUAL']
|
||||
items: tuple[Item, ...]
|
||||
enrollments: tuple[Enrollment, ...] | None = None
|
||||
coupon: Coupon | None = None
|
||||
user: User | None = None
|
||||
org_id: UUID4 | str | None = None
|
||||
|
||||
@@ -29,10 +29,13 @@ import {
|
||||
NativeSelect,
|
||||
NativeSelectOption
|
||||
} from '@repo/ui/components/ui/native-select'
|
||||
import { Currency } from '@repo/ui/components/currency'
|
||||
|
||||
import { useWizard } from '@/components/wizard'
|
||||
import { isName } from '../_.$orgid.users.add/data'
|
||||
import type { PaymentMethod } from '@repo/ui/routes/orders/data'
|
||||
import type { WizardState } from './route'
|
||||
import { applyDiscount } from './discount'
|
||||
|
||||
const creditCard = z.object({
|
||||
holder_name: z
|
||||
@@ -52,31 +55,32 @@ const creditCard = z.object({
|
||||
cvv: z.string().min(3).max(4)
|
||||
})
|
||||
|
||||
const formSchema = z.discriminatedUnion(
|
||||
'payment_method',
|
||||
[
|
||||
const formSchema = z.discriminatedUnion('payment_method', [
|
||||
z.object({
|
||||
payment_method: z.literal('PIX')
|
||||
}),
|
||||
|
||||
z.object({
|
||||
payment_method: z.literal('BANK_SLIP')
|
||||
}),
|
||||
|
||||
z.object({
|
||||
payment_method: z.literal('MANUAL')
|
||||
}),
|
||||
|
||||
z.object({
|
||||
payment_method: z.literal('CREDIT_CARD'),
|
||||
credit_card: creditCard
|
||||
credit_card: creditCard,
|
||||
installments: z.coerce.number().int().min(1).max(12)
|
||||
})
|
||||
],
|
||||
{ error: 'Escolha uma forma de pagamento' }
|
||||
)
|
||||
])
|
||||
|
||||
type Schema = z.infer<typeof formSchema>
|
||||
type Schema = z.input<typeof formSchema>
|
||||
|
||||
export type CreditCard = z.infer<typeof creditCard>
|
||||
|
||||
type PaymentProps = {
|
||||
state: WizardState
|
||||
onSubmit: (value: any) => void | Promise<void>
|
||||
payment_method?: PaymentMethod
|
||||
credit_card?: CreditCard
|
||||
@@ -84,6 +88,7 @@ type PaymentProps = {
|
||||
|
||||
export function Payment({
|
||||
onSubmit,
|
||||
state,
|
||||
payment_method: paymentMethodInit,
|
||||
credit_card: creditCardInit = undefined
|
||||
}: PaymentProps) {
|
||||
@@ -91,11 +96,23 @@ export function Payment({
|
||||
const { control, handleSubmit } = useForm<Schema>({
|
||||
defaultValues: {
|
||||
payment_method: paymentMethodInit,
|
||||
installments: state?.installments ?? 1,
|
||||
credit_card: creditCardInit
|
||||
},
|
||||
resolver: zodResolver(formSchema)
|
||||
})
|
||||
const paymentMethod = useWatch({ control, name: 'payment_method' })
|
||||
const subtotal = state.items.reduce(
|
||||
(acc, { course, quantity }) =>
|
||||
acc +
|
||||
(course?.unit_price || 0) *
|
||||
(Number.isFinite(quantity) && quantity > 0 ? quantity : 1),
|
||||
0
|
||||
)
|
||||
const discount = state.coupon
|
||||
? applyDiscount(subtotal, state.coupon.amount, state.coupon.type) * -1
|
||||
: 0
|
||||
const total = subtotal > 0 ? subtotal + discount : 0
|
||||
|
||||
const onSubmit_ = async (data: Schema) => {
|
||||
await onSubmit({ credit_card: undefined, ...data })
|
||||
@@ -147,7 +164,7 @@ export function Payment({
|
||||
/>
|
||||
|
||||
{paymentMethod === 'CREDIT_CARD' ? (
|
||||
<CreditCard control={control} />
|
||||
<CreditCard control={control} total={total} />
|
||||
) : null}
|
||||
|
||||
<Separator />
|
||||
@@ -171,7 +188,13 @@ export function Payment({
|
||||
)
|
||||
}
|
||||
|
||||
export function CreditCard({ control }: { control: Control<Schema> }) {
|
||||
export function CreditCard({
|
||||
total,
|
||||
control
|
||||
}: {
|
||||
total: number
|
||||
control: Control<Schema>
|
||||
}) {
|
||||
const currentYear = new Date().getFullYear()
|
||||
const years = Array.from({ length: 10 }, (_, i) => currentYear + i)
|
||||
|
||||
@@ -325,9 +348,58 @@ export function CreditCard({ control }: { control: Control<Schema> }) {
|
||||
)}
|
||||
/>
|
||||
</FieldSet>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="installments"
|
||||
defaultValue={1}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel htmlFor={field.name}>Parcelas</FieldLabel>
|
||||
<NativeSelect
|
||||
id={field.name}
|
||||
value={String(field.value)}
|
||||
onChange={field.onChange}
|
||||
aria-invalid={fieldState.invalid}
|
||||
>
|
||||
{Array.from({ length: 12 }, (_, index) => {
|
||||
const installment = index + 1 // 1 -> 12
|
||||
|
||||
if (installment === 1) {
|
||||
return (
|
||||
<NativeSelectOption key={installment} value={1}>
|
||||
<Currency>{total}</Currency> à vista
|
||||
</NativeSelectOption>
|
||||
)
|
||||
}
|
||||
|
||||
const value =
|
||||
calcInterest(total, installment) / installment
|
||||
|
||||
return (
|
||||
<NativeSelectOption
|
||||
key={installment}
|
||||
value={installment}
|
||||
>
|
||||
{installment}x <Currency>{value}</Currency> com juros
|
||||
</NativeSelectOption>
|
||||
)
|
||||
})}
|
||||
</NativeSelect>
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
</FieldSet>
|
||||
</FieldGroup>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export const calcInterest = (total: number, installment: number) => {
|
||||
const rate2to6 = 0.055
|
||||
const rate7to12 = 0.0608
|
||||
const rate = installment >= 7 ? rate7to12 : rate2to6
|
||||
|
||||
return total * ((1 - 0.0382) / (1 - rate))
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
InputGroupButton,
|
||||
InputGroupInput
|
||||
} from '@repo/ui/components/ui/input-group'
|
||||
import { calcInterest } from './payment'
|
||||
|
||||
type ReviewProps = {
|
||||
state: WizardState
|
||||
@@ -79,6 +80,13 @@ export function Review({ state, onSubmit }: ReviewProps) {
|
||||
? applyDiscount(subtotal, coupon.amount, coupon.type) * -1
|
||||
: 0
|
||||
const total = subtotal > 0 ? subtotal + discount : 0
|
||||
const installments = state.installments
|
||||
const interest_amount =
|
||||
state.payment_method === 'CREDIT_CARD' &&
|
||||
typeof installments === 'number' &&
|
||||
installments > 1
|
||||
? calcInterest(total, installments) - total
|
||||
: 0
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -117,6 +125,8 @@ export function Review({ state, onSubmit }: ReviewProps) {
|
||||
)
|
||||
})}
|
||||
</TableBody>
|
||||
|
||||
{/* Summary */}
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell className="text-right" colSpan={3}>
|
||||
@@ -126,6 +136,7 @@ export function Review({ state, onSubmit }: ReviewProps) {
|
||||
<Currency>{subtotal}</Currency>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/* Discount */}
|
||||
<TableRow>
|
||||
<TableCell className="text-right" colSpan={3}>
|
||||
Descontos
|
||||
@@ -134,12 +145,26 @@ export function Review({ state, onSubmit }: ReviewProps) {
|
||||
<Currency>{discount}</Currency>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/* Interest */}
|
||||
{interest_amount ? (
|
||||
<TableRow>
|
||||
<TableCell className="text-right" colSpan={3}>
|
||||
Juros
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Currency>{interest_amount}</Currency>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{/* Total */}
|
||||
<TableRow>
|
||||
<TableCell className="text-right" colSpan={3}>
|
||||
Total
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Currency>{total}</Currency>
|
||||
<Currency>{total + interest_amount}</Currency>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
|
||||
@@ -43,6 +43,7 @@ export type WizardState = {
|
||||
items: Item[]
|
||||
enrollments: Enrollment[]
|
||||
coupon?: Coupon
|
||||
installments?: number
|
||||
payment_method?: PaymentMethod
|
||||
credit_card?: CreditCard
|
||||
}
|
||||
@@ -54,6 +55,7 @@ const emptyWizard: WizardState = {
|
||||
enrollments: [],
|
||||
coupon: undefined,
|
||||
payment_method: undefined,
|
||||
installments: undefined,
|
||||
credit_card: undefined
|
||||
}
|
||||
|
||||
@@ -204,6 +206,7 @@ export default function Route({
|
||||
{/* Payment */}
|
||||
<WizardStep name="payment">
|
||||
<Payment
|
||||
state={state}
|
||||
payment_method={state.payment_method}
|
||||
credit_card={state.credit_card}
|
||||
onSubmit={(data: any) => {
|
||||
|
||||
Reference in New Issue
Block a user