fix: selfpayMode state vars + monolith.sh TPV port + closures report

- Add missing selfpayMode/closePin state vars in admin POS page
- Add selfpayMode/closeSessionRequiresPin/closeSessionPin to PosTerminal interface
- Fix ClosedSession type in reporting/closures to match cash-close endpoint
- Add smtpReportEmail to admin StoreSettings interface
- monolith.sh: add TPV service (port 3002), reorder ports:
  API 3000, ADMIN 3001, TPV 3002, FRONTEND 3003, STOREFRONT 3004
- Add apps/pos to install_dependencies in monolith.sh
This commit is contained in:
chattie
2026-08-23 09:41:21 +02:00
parent 57c1781789
commit 725a02527a
4 changed files with 43 additions and 16 deletions

View File

@@ -19,6 +19,9 @@ interface PosTerminal {
touchNavigationEnabled?: boolean; touchNavigationEnabled?: boolean;
quickProductVariantIds?: Array<string | null>; quickProductVariantIds?: Array<string | null>;
lineDiscountsEnabled?: boolean; lineDiscountsEnabled?: boolean;
selfpayMode?: boolean;
closeSessionRequiresPin?: boolean;
closeSessionPin?: string;
}; };
} }
interface CatalogOption { interface CatalogOption {
@@ -94,6 +97,12 @@ export default function PosAdminPage() {
); );
const [savingTouch, setSavingTouch] = useState(false); const [savingTouch, setSavingTouch] = useState(false);
const [touchMessage, setTouchMessage] = useState(''); const [touchMessage, setTouchMessage] = useState('');
// F-203: cash close security settings
const [selfpayMode, setSelfpayMode] = useState(false);
const [closePinRequired, setClosePinRequired] = useState(false);
const [closePinValue, setClosePinValue] = useState('');
const [savingSecurity, setSavingSecurity] = useState(false);
const [securityMessage, setSecurityMessage] = useState('');
const [paymentMethods, setPaymentMethods] = useState<PaymentMethod[]>([]); const [paymentMethods, setPaymentMethods] = useState<PaymentMethod[]>([]);
const [cashiers, setCashiers] = useState<PosCashier[]>([]); const [cashiers, setCashiers] = useState<PosCashier[]>([]);
const [newCashier, setNewCashier] = useState({ email: '', password: '' }); const [newCashier, setNewCashier] = useState({ email: '', password: '' });

View File

@@ -3,6 +3,16 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { api } from '@/lib/api-client'; import { api } from '@/lib/api-client';
interface SalesSummary {
totalCount: number;
completedCount: number;
completedTotalCents: number;
pendingCount: number;
refundedCount: number;
refundedTotalCents: number;
byState: Record<string, { count: number; totalCents: number }>;
}
interface ClosedSession { interface ClosedSession {
id: string; id: string;
openedAt: string; openedAt: string;
@@ -13,12 +23,9 @@ interface ClosedSession {
closingCashCents: number | null; closingCashCents: number | null;
actualCashCents: number | null; actualCashCents: number | null;
differenceCents: number | null; differenceCents: number | null;
salesCount: number; sales: SalesSummary;
salesTotalCents: number;
salesByState: Record<string, { count: number; totalCents: number }>;
paymentsByMethod: Array<{ methodCode: string; methodName: string; totalCents: number; count: number }>; paymentsByMethod: Array<{ methodCode: string; methodName: string; totalCents: number; count: number }>;
itemsSold: number; items: { soldCount: number; uniqueProducts: number };
uniqueProductsSold: number;
} }
function fmt(cents: number): string { function fmt(cents: number): string {
@@ -240,11 +247,11 @@ export default function ReportingClosuresPage() {
))} ))}
<div> <div>
<p className="text-xs text-gray-500">Artículos vendidos</p> <p className="text-xs text-gray-500">Artículos vendidos</p>
<p className="text-lg font-bold text-gray-900">{report.itemsSold}</p> <p className="text-lg font-bold text-gray-900">{report.items.soldCount}</p>
</div> </div>
<div> <div>
<p className="text-xs text-gray-500">Productos únicos</p> <p className="text-xs text-gray-500">Productos únicos</p>
<p className="text-lg font-bold text-gray-900">{report.uniqueProductsSold}</p> <p className="text-lg font-bold text-gray-900">{report.items.uniqueProducts}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -384,6 +384,7 @@ export interface StoreSettings {
smtpPass: string; smtpPass: string;
smtpPassConfigured?: boolean; smtpPassConfigured?: boolean;
smtpFrom: string; smtpFrom: string;
smtpReportEmail: string;
couriers?: string[]; couriers?: string[];
} }

View File

@@ -11,13 +11,14 @@ ACTION="${2:-}"
RUNTIME_ROOT="${MDV_RUNTIME_DIR:-$PROJECT_DIR/.runtime}" RUNTIME_ROOT="${MDV_RUNTIME_DIR:-$PROJECT_DIR/.runtime}"
RUNTIME_DIR="$RUNTIME_ROOT/$MODE" RUNTIME_DIR="$RUNTIME_ROOT/$MODE"
BACKEND_PORT="${BACKEND_PORT:-3000}" BACKEND_PORT="${BACKEND_PORT:-3000}"
ADMIN_PORT="${ADMIN_PORT:-3001}"
TPV_PORT="${TPV_PORT:-3002}"
FRONTEND_PORT="${FRONTEND_PORT:-3003}" FRONTEND_PORT="${FRONTEND_PORT:-3003}"
ADMIN_PORT="${ADMIN_PORT:-3004}" STOREFRONT_PORT="${STOREFRONT_PORT:-3004}"
STOREFRONT_PORT="${STOREFRONT_PORT:-3005}"
START_TIMEOUT="${START_TIMEOUT:-90}" START_TIMEOUT="${START_TIMEOUT:-90}"
WATCH_INTERVAL="${WATCH_INTERVAL:-5}" WATCH_INTERVAL="${WATCH_INTERVAL:-5}"
SERVICES=(backend frontend admin storefront) SERVICES=(backend admin tpv frontend storefront)
usage() { usage() {
cat <<'EOF' cat <<'EOF'
@@ -76,8 +77,9 @@ API_PUBLIC_URL="${API_PUBLIC_URL:-http://$LAN_ADDRESS:$BACKEND_PORT}"
service_port() { service_port() {
case "$1" in case "$1" in
backend) echo "$BACKEND_PORT" ;; backend) echo "$BACKEND_PORT" ;;
frontend) echo "$FRONTEND_PORT" ;;
admin) echo "$ADMIN_PORT" ;; admin) echo "$ADMIN_PORT" ;;
tpv) echo "$TPV_PORT" ;;
frontend) echo "$FRONTEND_PORT" ;;
storefront) echo "$STOREFRONT_PORT" ;; storefront) echo "$STOREFRONT_PORT" ;;
esac esac
} }
@@ -185,7 +187,7 @@ start_infrastructure() {
install_dependencies() { install_dependencies() {
local install_command=(npm install) local install_command=(npm install)
[[ "$MODE" == "prod" ]] && install_command=(npm ci) [[ "$MODE" == "prod" ]] && install_command=(npm ci)
for dir in "$PROJECT_DIR" "$PROJECT_DIR/apps/admin" "$PROJECT_DIR/frontend" "$PROJECT_DIR/storefront"; do for dir in "$PROJECT_DIR" "$PROJECT_DIR/apps/admin" "$PROJECT_DIR/apps/pos" "$PROJECT_DIR/frontend" "$PROJECT_DIR/storefront"; do
echo "[INFO] ${install_command[*]}${dir#$PROJECT_DIR/}" echo "[INFO] ${install_command[*]}${dir#$PROJECT_DIR/}"
(cd "$dir" && "${install_command[@]}") (cd "$dir" && "${install_command[@]}")
done done
@@ -257,6 +259,12 @@ spawn_service() {
prod:admin) prod:admin)
(cd "$PROJECT_DIR/apps/admin" && nohup env NEXT_PUBLIC_API_URL="$API_PUBLIC_URL" node node_modules/next/dist/bin/next start --hostname 0.0.0.0 --port "$port" >"$log" 2>&1 < /dev/null & disown; echo $! >"$pidfile") (cd "$PROJECT_DIR/apps/admin" && nohup env NEXT_PUBLIC_API_URL="$API_PUBLIC_URL" node node_modules/next/dist/bin/next start --hostname 0.0.0.0 --port "$port" >"$log" 2>&1 < /dev/null & disown; echo $! >"$pidfile")
;; ;;
dev:tpv)
(cd "$PROJECT_DIR/apps/pos" && nohup env NEXT_PUBLIC_API_URL="$API_PUBLIC_URL" node node_modules/next/dist/bin/next dev --hostname 0.0.0.0 --port "$port" >"$log" 2>&1 < /dev/null & disown; echo $! >"$pidfile")
;;
prod:tpv)
(cd "$PROJECT_DIR/apps/pos" && nohup env NEXT_PUBLIC_API_URL="$API_PUBLIC_URL" node node_modules/next/dist/bin/next start --hostname 0.0.0.0 --port "$port" >"$log" 2>&1 < /dev/null & disown; echo $! >"$pidfile")
;;
dev:frontend) dev:frontend)
(cd "$PROJECT_DIR/frontend" && nohup env NEXT_PUBLIC_API_URL="$API_PUBLIC_URL" node node_modules/next/dist/bin/next dev --hostname 0.0.0.0 --port "$port" >"$log" 2>&1 < /dev/null & disown; echo $! >"$pidfile") (cd "$PROJECT_DIR/frontend" && nohup env NEXT_PUBLIC_API_URL="$API_PUBLIC_URL" node node_modules/next/dist/bin/next dev --hostname 0.0.0.0 --port "$port" >"$log" 2>&1 < /dev/null & disown; echo $! >"$pidfile")
;; ;;
@@ -384,8 +392,10 @@ print_urls() {
Mode: $MODE Mode: $MODE
LAN IP: $LAN_ADDRESS LAN IP: $LAN_ADDRESS
Customer frontend: http://$LAN_ADDRESS:$FRONTEND_PORT/ Backend (API): http://$LAN_ADDRESS:$BACKEND_PORT/health
Admin backoffice: http://$LAN_ADDRESS:$ADMIN_PORT/ Admin backoffice: http://$LAN_ADDRESS:$ADMIN_PORT/
POS TPV: http://$LAN_ADDRESS:$TPV_PORT/
Customer frontend: http://$LAN_ADDRESS:$FRONTEND_PORT/
SEO storefront: http://$LAN_ADDRESS:$STOREFRONT_PORT/ SEO storefront: http://$LAN_ADDRESS:$STOREFRONT_PORT/
Backend health: http://$LAN_ADDRESS:$BACKEND_PORT/health Backend health: http://$LAN_ADDRESS:$BACKEND_PORT/health
Swagger/OpenAPI: http://$LAN_ADDRESS:$BACKEND_PORT/docs Swagger/OpenAPI: http://$LAN_ADDRESS:$BACKEND_PORT/docs