Bổ sung tính năng cập nhật tiền của địa điểm
This commit is contained in:
@@ -67,6 +67,9 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
|||||||
longitude: 106.7009,
|
longitude: 106.7009,
|
||||||
type: 'VISIT',
|
type: 'VISIT',
|
||||||
legId: '',
|
legId: '',
|
||||||
|
note: '',
|
||||||
|
expenseAmount: '',
|
||||||
|
expenseCategory: 'OTHER',
|
||||||
plannedStart: '',
|
plannedStart: '',
|
||||||
plannedEnd: ''
|
plannedEnd: ''
|
||||||
});
|
});
|
||||||
@@ -79,6 +82,9 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
if (editingLocation) {
|
if (editingLocation) {
|
||||||
|
const leg = legs.find(l => l.id === editingLocation.legId);
|
||||||
|
const expense = leg?.expenses?.find((e: any) => e.locationId === editingLocation.id);
|
||||||
|
|
||||||
setFormData({
|
setFormData({
|
||||||
name: editingLocation.name || '',
|
name: editingLocation.name || '',
|
||||||
address: editingLocation.address || '',
|
address: editingLocation.address || '',
|
||||||
@@ -86,6 +92,9 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
|||||||
longitude: editingLocation.longitude,
|
longitude: editingLocation.longitude,
|
||||||
type: editingLocation.type || 'VISIT',
|
type: editingLocation.type || 'VISIT',
|
||||||
legId: editingLocation.legId || '',
|
legId: editingLocation.legId || '',
|
||||||
|
note: editingLocation.note || '',
|
||||||
|
expenseAmount: expense?.amount?.toString() || '',
|
||||||
|
expenseCategory: expense?.category || 'OTHER',
|
||||||
plannedStart: editingLocation.plannedStart ? editingLocation.plannedStart.slice(0, 16) : '',
|
plannedStart: editingLocation.plannedStart ? editingLocation.plannedStart.slice(0, 16) : '',
|
||||||
plannedEnd: editingLocation.plannedEnd ? editingLocation.plannedEnd.slice(0, 16) : ''
|
plannedEnd: editingLocation.plannedEnd ? editingLocation.plannedEnd.slice(0, 16) : ''
|
||||||
});
|
});
|
||||||
@@ -94,6 +103,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
|||||||
...prev,
|
...prev,
|
||||||
name: '', address: '',
|
name: '', address: '',
|
||||||
legId: initialLegId || (legs.length > 0 ? legs[0].id : ''),
|
legId: initialLegId || (legs.length > 0 ? legs[0].id : ''),
|
||||||
|
note: '', expenseAmount: '', expenseCategory: 'OTHER',
|
||||||
plannedStart: '', plannedEnd: ''
|
plannedStart: '', plannedEnd: ''
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -195,6 +205,31 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
|||||||
<input className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
|
<input className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
|
||||||
value={formData.address} onChange={e => setFormData({...formData, address: e.target.value})} />
|
value={formData.address} onChange={e => setFormData({...formData, address: e.target.value})} />
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-gray-700 mb-1">Ghi chú / Dịch vụ sử dụng</label>
|
||||||
|
<textarea className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none resize-none" rows={2}
|
||||||
|
placeholder="Ví dụ: Ăn trưa tại quán X, thuê hướng dẫn viên..."
|
||||||
|
value={formData.note} onChange={e => setFormData({...formData, note: e.target.value})} />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-gray-700 mb-1">Số tiền (VNĐ)</label>
|
||||||
|
<input type="number" className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
|
||||||
|
placeholder="0"
|
||||||
|
value={formData.expenseAmount} onChange={e => setFormData({...formData, expenseAmount: e.target.value})} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-bold text-gray-700 mb-1">Loại dịch vụ</label>
|
||||||
|
<select className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
|
||||||
|
value={formData.expenseCategory} onChange={e => setFormData({...formData, expenseCategory: e.target.value})}>
|
||||||
|
<option value="FOOD">Ăn uống</option>
|
||||||
|
<option value="TRANSPORT">Di chuyển</option>
|
||||||
|
<option value="ACCOMMODATION">Chỗ ở</option>
|
||||||
|
<option value="TICKET">Vé tham quan</option>
|
||||||
|
<option value="OTHER">Khác</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-bold text-gray-700 mb-1">Gán vào chặng</label>
|
<label className="block text-sm font-bold text-gray-700 mb-1">Gán vào chặng</label>
|
||||||
<select required className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
|
<select required className="w-full px-4 py-3 bg-gray-50 border border-gray-100 rounded-xl outline-none"
|
||||||
|
|||||||
@@ -195,6 +195,8 @@ export const ItineraryTimeline = ({
|
|||||||
? differenceInMinutes(parseISO(location.plannedEnd), parseISO(location.plannedStart))
|
? differenceInMinutes(parseISO(location.plannedEnd), parseISO(location.plannedStart))
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const locationExpense = leg.expenses?.find((e: any) => e.locationId === location.id);
|
||||||
|
|
||||||
const isStartPoint = legs[0]?.id === leg.id && idx === 0;
|
const isStartPoint = legs[0]?.id === leg.id && idx === 0;
|
||||||
const isEndPoint = legs[legs.length - 1]?.id === leg.id && idx === leg.locations.length - 1;
|
const isEndPoint = legs[legs.length - 1]?.id === leg.id && idx === leg.locations.length - 1;
|
||||||
|
|
||||||
@@ -234,12 +236,23 @@ export const ItineraryTimeline = ({
|
|||||||
<MapPin className="w-3 h-3 mr-1" />
|
<MapPin className="w-3 h-3 mr-1" />
|
||||||
<span className="truncate max-w-[200px] sm:max-w-md">{location.address}</span>
|
<span className="truncate max-w-[200px] sm:max-w-md">{location.address}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{location.note && (
|
||||||
|
<div className="mt-2 text-xs text-gray-600 bg-gray-50 p-2 rounded-lg border border-gray-100 italic">
|
||||||
|
{location.note}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{dwellMinutes !== null && (
|
{dwellMinutes !== null && (
|
||||||
<div className="flex items-center text-xs text-amber-600 font-medium mt-1">
|
<div className="flex items-center text-xs text-amber-600 font-medium mt-1">
|
||||||
<Clock className="w-3 h-3 mr-1" />
|
<Clock className="w-3 h-3 mr-1" />
|
||||||
<span>Thời gian dừng: {formatTravelTime(dwellMinutes)}</span>
|
<span>Thời gian dừng: {formatTravelTime(dwellMinutes)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{locationExpense && (
|
||||||
|
<div className="flex items-center text-xs text-indigo-600 font-bold mt-1">
|
||||||
|
<Zap className="w-3 h-3 mr-1" />
|
||||||
|
<span>Chi phí: {Number(locationExpense.amount).toLocaleString()}đ ({locationExpense.category})</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-right flex flex-col items-end">
|
<div className="text-right flex flex-col items-end">
|
||||||
|
|||||||
Vendored
+10
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-1
@@ -86,9 +86,10 @@ export const ItineraryTimeline = ({ onAddLocation, onEditLocation }) => {
|
|||||||
const dwellMinutes = (location.plannedStart && location.plannedEnd)
|
const dwellMinutes = (location.plannedStart && location.plannedEnd)
|
||||||
? differenceInMinutes(parseISO(location.plannedEnd), parseISO(location.plannedStart))
|
? differenceInMinutes(parseISO(location.plannedEnd), parseISO(location.plannedStart))
|
||||||
: null;
|
: null;
|
||||||
|
const locationExpense = leg.expenses?.find((e) => e.locationId === location.id);
|
||||||
const isStartPoint = legs[0]?.id === leg.id && idx === 0;
|
const isStartPoint = legs[0]?.id === leg.id && idx === 0;
|
||||||
const isEndPoint = legs[legs.length - 1]?.id === leg.id && idx === leg.locations.length - 1;
|
const isEndPoint = legs[legs.length - 1]?.id === leg.id && idx === leg.locations.length - 1;
|
||||||
return (_jsxs("div", { children: [_jsxs("div", { className: "relative flex group mb-6", children: [_jsx("div", { className: "z-10 mt-1.5 mr-4", children: _jsx("button", { onClick: () => toggleComplete(location.id), className: `transition-colors duration-200 ${location.status === 'COMPLETED' ? 'text-green-500' : 'text-gray-300 hover:text-blue-500'}`, children: location.status === 'COMPLETED' ? (_jsx(CheckCircle2, { className: "w-8 h-8 bg-white rounded-full" })) : (_jsx(Circle, { className: "w-8 h-8 bg-white rounded-full fill-white" })) }) }), _jsxs("div", { className: `flex-1 bg-white p-4 rounded-xl border transition-all duration-200 ${location.status === 'COMPLETED' ? 'border-green-100 bg-green-50/30' : 'border-gray-100 shadow-sm hover:shadow-md'}`, children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsxs("div", { children: [isStartPoint && (_jsx("span", { className: "inline-block px-2 py-0.5 bg-green-100 text-green-700 text-[10px] font-bold rounded-md mb-1 uppercase tracking-wider", children: "\u0110i\u1EC3m b\u1EAFt \u0111\u1EA7u" })), isEndPoint && (_jsx("span", { className: "inline-block px-2 py-0.5 bg-red-100 text-red-700 text-[10px] font-bold rounded-md mb-1 uppercase tracking-wider", children: "\u0110i\u1EC3m k\u1EBFt th\u00FAc" })), _jsx("h3", { className: `font-semibold text-lg ${location.status === 'COMPLETED' ? 'text-gray-500 line-through' : 'text-gray-800'}`, children: location.name }), _jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [_jsx(MapPin, { className: "w-3 h-3 mr-1" }), _jsx("span", { className: "truncate max-w-[200px] sm:max-w-md", children: location.address })] }), dwellMinutes !== null && (_jsxs("div", { className: "flex items-center text-xs text-amber-600 font-medium mt-1", children: [_jsx(Clock, { className: "w-3 h-3 mr-1" }), _jsxs("span", { children: ["Th\u1EDDi gian d\u1EEBng: ", formatTravelTime(dwellMinutes)] })] }))] }), _jsxs("div", { className: "text-right flex flex-col items-end", children: [_jsxs("div", { className: "flex items-center text-sm font-medium text-blue-600", children: [_jsx(Clock, { className: "w-3 h-3 mr-1" }), location.plannedStart ? format(parseISO(location.plannedStart), 'HH:mm') : '--:--'] }), location.status === 'COMPLETED' && location.actualStart && (_jsxs("div", { className: "text-[10px] text-gray-400 mt-1 italic", children: ["Th\u1EF1c t\u1EBF: ", format(parseISO(location.actualStart), 'HH:mm')] })), ['OWNER', 'MANAGER'].includes(userRole || '') && !isStartPoint && !isEndPoint && (_jsxs("div", { className: "flex gap-1 mt-2", children: [_jsx("button", { onClick: () => onEditLocation?.(location), className: "p-1 text-gray-400 hover:text-blue-600 transition-colors", children: _jsx(Edit2, { className: "w-3.5 h-3.5" }) }), _jsx("button", { onClick: () => handleDeleteLocation(location.id), className: "p-1 text-gray-400 hover:text-red-600 transition-colors", children: _jsx(Trash2, { className: "w-3.5 h-3.5" }) })] }))] })] }), _jsx(TimeVariance, { planned: location.plannedStart || '', actual: location.actualStart || null })] })] }), distanceToNext !== null && travelTimeMinutes !== null && (_jsxs("div", { className: "ml-4 -mt-4 mb-2 flex items-center gap-3", children: [_jsx("div", { className: "w-8 flex justify-center", children: _jsx(Navigation, { className: "w-3 h-3 text-blue-400 rotate-180" }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-[10px] font-bold text-blue-500 bg-blue-50 px-2 py-0.5 rounded-full border border-blue-100", children: [distanceToNext.toFixed(2), " km"] }), _jsxs("span", { className: "text-[10px] font-bold text-gray-500 bg-gray-50 px-2 py-0.5 rounded-full border border-gray-100 flex items-center gap-1", children: [_jsx(Clock, { className: "w-2.5 h-2.5" }), "~ ", formatTravelTime(travelTimeMinutes)] })] })] }))] }, location.id));
|
return (_jsxs("div", { children: [_jsxs("div", { className: "relative flex group mb-6", children: [_jsx("div", { className: "z-10 mt-1.5 mr-4", children: _jsx("button", { onClick: () => toggleComplete(location.id), className: `transition-colors duration-200 ${location.status === 'COMPLETED' ? 'text-green-500' : 'text-gray-300 hover:text-blue-500'}`, children: location.status === 'COMPLETED' ? (_jsx(CheckCircle2, { className: "w-8 h-8 bg-white rounded-full" })) : (_jsx(Circle, { className: "w-8 h-8 bg-white rounded-full fill-white" })) }) }), _jsxs("div", { className: `flex-1 bg-white p-4 rounded-xl border transition-all duration-200 ${location.status === 'COMPLETED' ? 'border-green-100 bg-green-50/30' : 'border-gray-100 shadow-sm hover:shadow-md'}`, children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsxs("div", { children: [isStartPoint && (_jsx("span", { className: "inline-block px-2 py-0.5 bg-green-100 text-green-700 text-[10px] font-bold rounded-md mb-1 uppercase tracking-wider", children: "\u0110i\u1EC3m b\u1EAFt \u0111\u1EA7u" })), isEndPoint && (_jsx("span", { className: "inline-block px-2 py-0.5 bg-red-100 text-red-700 text-[10px] font-bold rounded-md mb-1 uppercase tracking-wider", children: "\u0110i\u1EC3m k\u1EBFt th\u00FAc" })), _jsx("h3", { className: `font-semibold text-lg ${location.status === 'COMPLETED' ? 'text-gray-500 line-through' : 'text-gray-800'}`, children: location.name }), _jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [_jsx(MapPin, { className: "w-3 h-3 mr-1" }), _jsx("span", { className: "truncate max-w-[200px] sm:max-w-md", children: location.address })] }), location.note && (_jsx("div", { className: "mt-2 text-xs text-gray-600 bg-gray-50 p-2 rounded-lg border border-gray-100 italic", children: location.note })), dwellMinutes !== null && (_jsxs("div", { className: "flex items-center text-xs text-amber-600 font-medium mt-1", children: [_jsx(Clock, { className: "w-3 h-3 mr-1" }), _jsxs("span", { children: ["Th\u1EDDi gian d\u1EEBng: ", formatTravelTime(dwellMinutes)] })] })), locationExpense && (_jsxs("div", { className: "flex items-center text-xs text-indigo-600 font-bold mt-1", children: [_jsx(Zap, { className: "w-3 h-3 mr-1" }), _jsxs("span", { children: ["Chi ph\u00ED: ", Number(locationExpense.amount).toLocaleString(), "\u0111 (", locationExpense.category, ")"] })] }))] }), _jsxs("div", { className: "text-right flex flex-col items-end", children: [_jsxs("div", { className: "flex items-center text-sm font-medium text-blue-600", children: [_jsx(Clock, { className: "w-3 h-3 mr-1" }), location.plannedStart ? format(parseISO(location.plannedStart), 'HH:mm') : '--:--'] }), location.status === 'COMPLETED' && location.actualStart && (_jsxs("div", { className: "text-[10px] text-gray-400 mt-1 italic", children: ["Th\u1EF1c t\u1EBF: ", format(parseISO(location.actualStart), 'HH:mm')] })), ['OWNER', 'MANAGER'].includes(userRole || '') && !isStartPoint && !isEndPoint && (_jsxs("div", { className: "flex gap-1 mt-2", children: [_jsx("button", { onClick: () => onEditLocation?.(location), className: "p-1 text-gray-400 hover:text-blue-600 transition-colors", children: _jsx(Edit2, { className: "w-3.5 h-3.5" }) }), _jsx("button", { onClick: () => handleDeleteLocation(location.id), className: "p-1 text-gray-400 hover:text-red-600 transition-colors", children: _jsx(Trash2, { className: "w-3.5 h-3.5" }) })] }))] })] }), _jsx(TimeVariance, { planned: location.plannedStart || '', actual: location.actualStart || null })] })] }), distanceToNext !== null && travelTimeMinutes !== null && (_jsxs("div", { className: "ml-4 -mt-4 mb-2 flex items-center gap-3", children: [_jsx("div", { className: "w-8 flex justify-center", children: _jsx(Navigation, { className: "w-3 h-3 text-blue-400 rotate-180" }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-[10px] font-bold text-blue-500 bg-blue-50 px-2 py-0.5 rounded-full border border-blue-100", children: [distanceToNext.toFixed(2), " km"] }), _jsxs("span", { className: "text-[10px] font-bold text-gray-500 bg-gray-50 px-2 py-0.5 rounded-full border border-gray-100 flex items-center gap-1", children: [_jsx(Clock, { className: "w-2.5 h-2.5" }), "~ ", formatTravelTime(travelTimeMinutes)] })] })] }))] }, location.id));
|
||||||
}) })] }, leg.id));
|
}) })] }, leg.id));
|
||||||
})), ['OWNER', 'MANAGER'].includes(userRole || '') && (_jsxs("div", { className: "flex flex-col gap-3 pb-20 mt-8", children: [_jsxs("button", { onClick: handleDeclareLegs, className: "w-full py-4 rounded-2xl bg-white text-blue-600 border-2 border-dashed border-blue-200 hover:bg-blue-50 transition-all flex items-center justify-center gap-2 text-sm font-bold", children: [_jsx(List, { className: "w-5 h-5" }), legs.length > 0 ? "Khai báo lại số lượng chặng" : "Bắt đầu bằng việc khai báo số chặng"] }), _jsxs("button", { onClick: handleAddLeg, className: "w-full py-4 rounded-2xl bg-blue-600 text-white shadow-lg shadow-blue-100 hover:bg-blue-700 transition-all flex items-center justify-center gap-2 text-sm font-bold", children: [_jsx(Plus, { className: "w-5 h-5" }), " Th\u00EAm ch\u1EB7ng l\u1EBB v\u00E0o cu\u1ED1i"] })] }))] }) }));
|
})), ['OWNER', 'MANAGER'].includes(userRole || '') && (_jsxs("div", { className: "flex flex-col gap-3 pb-20 mt-8", children: [_jsxs("button", { onClick: handleDeclareLegs, className: "w-full py-4 rounded-2xl bg-white text-blue-600 border-2 border-dashed border-blue-200 hover:bg-blue-50 transition-all flex items-center justify-center gap-2 text-sm font-bold", children: [_jsx(List, { className: "w-5 h-5" }), legs.length > 0 ? "Khai báo lại số lượng chặng" : "Bắt đầu bằng việc khai báo số chặng"] }), _jsxs("button", { onClick: handleAddLeg, className: "w-full py-4 rounded-2xl bg-blue-600 text-white shadow-lg shadow-blue-100 hover:bg-blue-700 transition-all flex items-center justify-center gap-2 text-sm font-bold", children: [_jsx(Plus, { className: "w-5 h-5" }), " Th\u00EAm ch\u1EB7ng l\u1EBB v\u00E0o cu\u1ED1i"] })] }))] }) }));
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+47
-9
@@ -148,6 +148,19 @@ let TourController = class TourController {
|
|||||||
plannedStart: body.plannedStart ? new Date(body.plannedStart) : null,
|
plannedStart: body.plannedStart ? new Date(body.plannedStart) : null,
|
||||||
plannedEnd: body.plannedEnd ? new Date(body.plannedEnd) : null,
|
plannedEnd: body.plannedEnd ? new Date(body.plannedEnd) : null,
|
||||||
}
|
}
|
||||||
|
}).then(async (loc) => {
|
||||||
|
if (body.expenseAmount && Number(body.expenseAmount) > 0) {
|
||||||
|
await this.prisma.expense.create({
|
||||||
|
data: {
|
||||||
|
amount: Number(body.expenseAmount),
|
||||||
|
category: body.expenseCategory || 'OTHER',
|
||||||
|
locationId: loc.id,
|
||||||
|
legId: loc.legId,
|
||||||
|
description: `Chi phí tại ${loc.name}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return loc;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async updateTourStartPoint(tourId, body, req) {
|
async updateTourStartPoint(tourId, body, req) {
|
||||||
@@ -396,19 +409,44 @@ let LocationController = class LocationController {
|
|||||||
this.prisma = prisma;
|
this.prisma = prisma;
|
||||||
}
|
}
|
||||||
async updateLocation(id, body) {
|
async updateLocation(id, body) {
|
||||||
return this.prisma.location.update({
|
const { expenseAmount, expenseCategory, ...data } = body;
|
||||||
|
const location = await this.prisma.location.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
name: body.name,
|
name: data.name,
|
||||||
address: body.address,
|
address: data.address,
|
||||||
latitude: body.latitude,
|
latitude: data.latitude,
|
||||||
longitude: body.longitude,
|
longitude: data.longitude,
|
||||||
type: body.type,
|
type: data.type,
|
||||||
plannedStart: body.plannedStart ? new Date(body.plannedStart) : undefined,
|
plannedStart: data.plannedStart ? new Date(data.plannedStart) : undefined,
|
||||||
plannedEnd: body.plannedEnd ? new Date(body.plannedEnd) : undefined,
|
plannedEnd: data.plannedEnd ? new Date(data.plannedEnd) : undefined,
|
||||||
status: body.status,
|
status: data.status,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (expenseAmount !== undefined) {
|
||||||
|
const amount = Number(expenseAmount);
|
||||||
|
const existingExpense = await this.prisma.expense.findFirst({
|
||||||
|
where: { locationId: id }
|
||||||
|
});
|
||||||
|
if (existingExpense) {
|
||||||
|
await this.prisma.expense.update({
|
||||||
|
where: { id: existingExpense.id },
|
||||||
|
data: { amount, category: expenseCategory || 'OTHER' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (amount > 0) {
|
||||||
|
await this.prisma.expense.create({
|
||||||
|
data: {
|
||||||
|
amount,
|
||||||
|
category: expenseCategory || 'OTHER',
|
||||||
|
locationId: id,
|
||||||
|
legId: location.legId,
|
||||||
|
description: `Chi phí tại ${location.name}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return location;
|
||||||
}
|
}
|
||||||
async deleteLocation(id) {
|
async deleteLocation(id) {
|
||||||
await this.prisma.location.delete({ where: { id } });
|
await this.prisma.location.delete({ where: { id } });
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -132,6 +132,20 @@ class TourController {
|
|||||||
plannedStart: body.plannedStart ? new Date(body.plannedStart) : null,
|
plannedStart: body.plannedStart ? new Date(body.plannedStart) : null,
|
||||||
plannedEnd: body.plannedEnd ? new Date(body.plannedEnd) : null,
|
plannedEnd: body.plannedEnd ? new Date(body.plannedEnd) : null,
|
||||||
}
|
}
|
||||||
|
}).then(async (loc) => {
|
||||||
|
// Thêm nhanh chi phí nếu có số tiền
|
||||||
|
if (body.expenseAmount && Number(body.expenseAmount) > 0) {
|
||||||
|
await this.prisma.expense.create({
|
||||||
|
data: {
|
||||||
|
amount: Number(body.expenseAmount),
|
||||||
|
category: body.expenseCategory || 'OTHER',
|
||||||
|
locationId: loc.id,
|
||||||
|
legId: loc.legId,
|
||||||
|
description: `Chi phí tại ${loc.name}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return loc;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,19 +354,47 @@ class LocationController {
|
|||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
async updateLocation(@Param('id', ParseUUIDPipe) id: string, @Body() body: any) {
|
async updateLocation(@Param('id', ParseUUIDPipe) id: string, @Body() body: any) {
|
||||||
return this.prisma.location.update({
|
const { expenseAmount, expenseCategory, ...data } = body;
|
||||||
|
|
||||||
|
const location = await this.prisma.location.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: {
|
data: {
|
||||||
name: body.name,
|
name: data.name,
|
||||||
address: body.address,
|
address: data.address,
|
||||||
latitude: body.latitude,
|
latitude: data.latitude,
|
||||||
longitude: body.longitude,
|
longitude: data.longitude,
|
||||||
type: body.type,
|
type: data.type,
|
||||||
plannedStart: body.plannedStart ? new Date(body.plannedStart) : undefined,
|
plannedStart: data.plannedStart ? new Date(data.plannedStart) : undefined,
|
||||||
plannedEnd: body.plannedEnd ? new Date(body.plannedEnd) : undefined,
|
plannedEnd: data.plannedEnd ? new Date(data.plannedEnd) : undefined,
|
||||||
status: body.status,
|
status: data.status,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (expenseAmount !== undefined) {
|
||||||
|
const amount = Number(expenseAmount);
|
||||||
|
const existingExpense = await this.prisma.expense.findFirst({
|
||||||
|
where: { locationId: id }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingExpense) {
|
||||||
|
await this.prisma.expense.update({
|
||||||
|
where: { id: existingExpense.id },
|
||||||
|
data: { amount, category: expenseCategory || 'OTHER' }
|
||||||
|
});
|
||||||
|
} else if (amount > 0) {
|
||||||
|
await this.prisma.expense.create({
|
||||||
|
data: {
|
||||||
|
amount,
|
||||||
|
category: expenseCategory || 'OTHER',
|
||||||
|
locationId: id,
|
||||||
|
legId: location.legId,
|
||||||
|
description: `Chi phí tại ${location.name}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
|||||||
Reference in New Issue
Block a user