Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d932d9af1c | ||
|
|
6d6d127126 | ||
|
|
da9fdc25c4 |
@@ -506,11 +506,13 @@ function SubmissionRow({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentBalance !== undefined) {
|
if (currentBalance !== undefined) {
|
||||||
let balanceBeforeThisOrder = currentBalance
|
let balanceBeforeThisOrder = currentBalance
|
||||||
if (sub.payment_status === 'LUNAS') {
|
const shouldCountBill = sub.bill != null && (sub.order.status === 'CLOSE' || sub.payment_status === 'LUNAS')
|
||||||
|
if (shouldCountBill) {
|
||||||
const diff = (sub.paid_amount || 0) - (sub.bill || 0)
|
const diff = (sub.paid_amount || 0) - (sub.bill || 0)
|
||||||
balanceBeforeThisOrder -= diff
|
balanceBeforeThisOrder -= diff
|
||||||
} else {
|
} else {
|
||||||
balanceBeforeThisOrder += sub.saldo_used || 0
|
const diff = (sub.paid_amount || 0) - (sub.saldo_used || 0)
|
||||||
|
balanceBeforeThisOrder -= diff
|
||||||
}
|
}
|
||||||
setAvailableBalance(Math.max(0, balanceBeforeThisOrder))
|
setAvailableBalance(Math.max(0, balanceBeforeThisOrder))
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-2
@@ -3,7 +3,8 @@
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { revalidatePath } from 'next/cache'
|
import { revalidatePath } from 'next/cache'
|
||||||
|
|
||||||
import { hashPassword, verifyPassword, encrypt, decrypt } from '@/lib/auth'
|
import { encrypt, decrypt } from '@/lib/auth'
|
||||||
|
import { hashPassword, verifyPassword } from '@/lib/password'
|
||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
import { sendMattermostNotification } from '@/lib/mattermost'
|
import { sendMattermostNotification } from '@/lib/mattermost'
|
||||||
|
|
||||||
@@ -438,6 +439,19 @@ export async function processBulkPayment(data: {
|
|||||||
// Sort by order date ascending (oldest first)
|
// Sort by order date ascending (oldest first)
|
||||||
submissions.sort((a, b) => new Date(a.order.date).getTime() - new Date(b.order.date).getTime())
|
submissions.sort((a, b) => new Date(a.order.date).getTime() - new Date(b.order.date).getTime())
|
||||||
|
|
||||||
|
// Reverse the effect of these submissions on currentBalance to get true available balance
|
||||||
|
if (data.use_balance) {
|
||||||
|
for (const sub of submissions) {
|
||||||
|
const shouldCountBill = sub.bill != null && (sub.order.status === 'CLOSE' || sub.payment_status === 'LUNAS')
|
||||||
|
if (shouldCountBill) {
|
||||||
|
currentBalance -= ((sub.paid_amount || 0) - (sub.bill || 0))
|
||||||
|
} else {
|
||||||
|
currentBalance -= ((sub.paid_amount || 0) - (sub.saldo_used || 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentBalance = Math.max(0, currentBalance)
|
||||||
|
}
|
||||||
|
|
||||||
let remainingBalance = currentBalance
|
let remainingBalance = currentBalance
|
||||||
let remainingCash = data.cash_amount
|
let remainingCash = data.cash_amount
|
||||||
let totalAvailable = remainingBalance + remainingCash
|
let totalAvailable = remainingBalance + remainingCash
|
||||||
@@ -948,7 +962,7 @@ export async function getPiutangSummary(creator_id: string) {
|
|||||||
select: { id: true, name: true, photo: true },
|
select: { id: true, name: true, photo: true },
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
select: { id: true, title: true, date: true },
|
select: { id: true, title: true, date: true, status: true },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: {
|
||||||
|
|||||||
@@ -96,7 +96,21 @@ export default function PiutangManager({ creatorId }: { creatorId: string }) {
|
|||||||
u.user.name.toLowerCase().includes(searchQuery.toLowerCase())
|
u.user.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
)
|
)
|
||||||
|
|
||||||
const targetUserBalance = balancesData.find((b) => b.user.id === target.userId)?.amount || 0
|
let rawTargetUserBalance = balancesData.find((b) => b.user.id === target.userId)?.amount || 0
|
||||||
|
const userObj = data.find((u) => u.user.id === target.userId)
|
||||||
|
|
||||||
|
if (userObj) {
|
||||||
|
userObj.submissions.forEach((sub: any) => {
|
||||||
|
const shouldCountBill = sub.bill != null && (sub.order.status === 'CLOSE' || sub.payment_status === 'LUNAS')
|
||||||
|
if (shouldCountBill) {
|
||||||
|
rawTargetUserBalance -= ((sub.paid_amount || 0) - (sub.bill || 0))
|
||||||
|
} else {
|
||||||
|
rawTargetUserBalance -= ((sub.paid_amount || 0) - (sub.saldo_used || 0))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUserBalance = Math.max(0, rawTargetUserBalance)
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export default function ReportByPersonGrid({
|
|||||||
const unpaid = Math.max(0, bill - paid)
|
const unpaid = Math.max(0, bill - paid)
|
||||||
obj.totalPiutang += unpaid
|
obj.totalPiutang += unpaid
|
||||||
obj.totalPaid += paid
|
obj.totalPaid += paid
|
||||||
obj.submissions.push({ ...sub, orderTitle: order.title, orderDate: order.date, paid, unpaid })
|
obj.submissions.push({ ...sub, orderTitle: order.title, orderDate: order.date, orderStatus: order.status, paid, unpaid })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -148,8 +148,24 @@ export default function ReportByPersonGrid({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetUserBalanceObj = balancesData.find((b) => b.user.id === target.userId)
|
let rawTargetUserBalance = balancesData.find((b) => b.user.id === target.userId)?.amount || 0
|
||||||
const targetUserBalance = targetUserBalanceObj ? targetUserBalanceObj.amount : 0
|
const userObj = userList.find((u) => u.user.id === target.userId)
|
||||||
|
|
||||||
|
if (userObj) {
|
||||||
|
userObj.submissions.forEach((sub: any) => {
|
||||||
|
// Only add back submissions that are part of the payment target
|
||||||
|
if (target.ids.includes(sub.id)) {
|
||||||
|
const shouldCountBill = sub.bill != null && (sub.orderStatus === 'CLOSE' || sub.payment_status === 'LUNAS')
|
||||||
|
if (shouldCountBill) {
|
||||||
|
rawTargetUserBalance -= ((sub.paid_amount || 0) - (sub.bill || 0))
|
||||||
|
} else {
|
||||||
|
rawTargetUserBalance -= ((sub.paid_amount || 0) - (sub.saldo_used || 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUserBalance = Math.max(0, rawTargetUserBalance)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
import { SignJWT, jwtVerify } from 'jose'
|
import { SignJWT, jwtVerify } from 'jose'
|
||||||
import bcrypt from 'bcryptjs'
|
|
||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
|
|
||||||
const secretKey = process.env.JWT_SECRET
|
const secretKey = process.env.JWT_SECRET
|
||||||
const key = new TextEncoder().encode(secretKey)
|
const key = new TextEncoder().encode(secretKey)
|
||||||
|
|
||||||
export async function hashPassword(password: string) {
|
|
||||||
return await bcrypt.hash(password, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function verifyPassword(password: string, hash: string) {
|
|
||||||
return await bcrypt.compare(password, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function encrypt(payload: any) {
|
export async function encrypt(payload: any) {
|
||||||
return await new SignJWT(payload).setProtectedHeader({ alg: 'HS256' }).setIssuedAt().sign(key) // No expiration as requested
|
return await new SignJWT(payload).setProtectedHeader({ alg: 'HS256' }).setIssuedAt().sign(key) // No expiration as requested
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import bcrypt from 'bcryptjs'
|
||||||
|
|
||||||
|
export async function hashPassword(password: string) {
|
||||||
|
return await bcrypt.hash(password, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyPassword(password: string, hash: string) {
|
||||||
|
return await bcrypt.compare(password, hash)
|
||||||
|
}
|
||||||
+7
-4
@@ -2,14 +2,17 @@ import { NextResponse } from 'next/server'
|
|||||||
import type { NextRequest } from 'next/server'
|
import type { NextRequest } from 'next/server'
|
||||||
import { decrypt } from '@/lib/auth'
|
import { decrypt } from '@/lib/auth'
|
||||||
|
|
||||||
const protectedRoutes = ['/', '/my-orders', '/my-purchases', '/reports', '/profile', '/users']
|
const protectedRoutes = ['/', '/my-orders', '/my-purchases', '/reports', '/profile', '/users', '/balances']
|
||||||
|
const protectedPrefixes = ['/my-orders/', '/order/', '/settings/']
|
||||||
const publicRoutes = ['/login', '/register']
|
const publicRoutes = ['/login', '/register']
|
||||||
|
|
||||||
export async function middleware(request: NextRequest) {
|
export async function middleware(request: NextRequest) {
|
||||||
const path = request.nextUrl.pathname
|
const path = request.nextUrl.pathname
|
||||||
|
|
||||||
// Also protect dynamic routes like /my-orders/[id]
|
const isProtectedRoute =
|
||||||
const isProtectedRoute = protectedRoutes.includes(path) || path.startsWith('/my-orders/')
|
protectedRoutes.includes(path) ||
|
||||||
|
protectedPrefixes.some(prefix => path.startsWith(prefix))
|
||||||
|
|
||||||
const isPublicRoute = publicRoutes.includes(path)
|
const isPublicRoute = publicRoutes.includes(path)
|
||||||
|
|
||||||
const cookie = request.cookies.get('session')?.value
|
const cookie = request.cookies.get('session')?.value
|
||||||
@@ -23,7 +26,7 @@ export async function middleware(request: NextRequest) {
|
|||||||
return NextResponse.redirect(new URL('/', request.nextUrl))
|
return NextResponse.redirect(new URL('/', request.nextUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.startsWith('/users') && session?.role !== 'superadmin') {
|
if (path.startsWith('/settings') && session?.role !== 'superadmin') {
|
||||||
return NextResponse.redirect(new URL('/', request.nextUrl))
|
return NextResponse.redirect(new URL('/', request.nextUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user