457 lines
16 KiB
TypeScript
457 lines
16 KiB
TypeScript
|
|
import React, { useState, useEffect } from "react";
|
|||
|
|
import { FaCreditCard, FaSave, FaTimes } from "react-icons/fa";
|
|||
|
|
import {
|
|||
|
|
FiCheck,
|
|||
|
|
CheckTypeEnum,
|
|||
|
|
CheckStatusEnum,
|
|||
|
|
FiCurrentAccount,
|
|||
|
|
} from "../../../types/fi";
|
|||
|
|
|
|||
|
|
interface CheckFormProps {
|
|||
|
|
check?: FiCheck;
|
|||
|
|
currentAccounts: FiCurrentAccount[];
|
|||
|
|
onSave: (
|
|||
|
|
check: Omit<FiCheck, "id" | "creationTime" | "lastModificationTime">
|
|||
|
|
) => void;
|
|||
|
|
onCancel: () => void;
|
|||
|
|
isOpen: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const CheckForm: React.FC<CheckFormProps> = ({
|
|||
|
|
check,
|
|||
|
|
currentAccounts,
|
|||
|
|
onSave,
|
|||
|
|
onCancel,
|
|||
|
|
isOpen,
|
|||
|
|
}) => {
|
|||
|
|
const [formData, setFormData] = useState({
|
|||
|
|
checkNumber: "",
|
|||
|
|
bankName: "",
|
|||
|
|
branchName: "",
|
|||
|
|
accountNumber: "",
|
|||
|
|
drawerName: "",
|
|||
|
|
payeeName: "",
|
|||
|
|
currentAccountId: "",
|
|||
|
|
issueDate: new Date().toISOString().split("T")[0],
|
|||
|
|
dueDate: new Date().toISOString().split("T")[0],
|
|||
|
|
amount: 0,
|
|||
|
|
currency: "TRY",
|
|||
|
|
status: CheckStatusEnum.InHand,
|
|||
|
|
type: CheckTypeEnum.Received,
|
|||
|
|
notes: "",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (check) {
|
|||
|
|
setFormData({
|
|||
|
|
checkNumber: check.checkNumber,
|
|||
|
|
bankName: check.bankName,
|
|||
|
|
branchName: check.branchName,
|
|||
|
|
accountNumber: check.accountNumber,
|
|||
|
|
drawerName: check.drawerName,
|
|||
|
|
payeeName: check.payeeName,
|
|||
|
|
currentAccountId: check.currentAccountId || "",
|
|||
|
|
issueDate: new Date(check.issueDate).toISOString().split("T")[0],
|
|||
|
|
dueDate: new Date(check.dueDate).toISOString().split("T")[0],
|
|||
|
|
amount: check.amount,
|
|||
|
|
currency: check.currency,
|
|||
|
|
status: check.status,
|
|||
|
|
type: check.type,
|
|||
|
|
notes: check.notes || "",
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
setFormData({
|
|||
|
|
checkNumber: "",
|
|||
|
|
bankName: "",
|
|||
|
|
branchName: "",
|
|||
|
|
accountNumber: "",
|
|||
|
|
drawerName: "",
|
|||
|
|
payeeName: "",
|
|||
|
|
currentAccountId: "",
|
|||
|
|
issueDate: new Date().toISOString().split("T")[0],
|
|||
|
|
dueDate: new Date().toISOString().split("T")[0],
|
|||
|
|
amount: 0,
|
|||
|
|
currency: "TRY",
|
|||
|
|
status: CheckStatusEnum.InHand,
|
|||
|
|
type: CheckTypeEnum.Received,
|
|||
|
|
notes: "",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
setErrors({});
|
|||
|
|
}, [check, isOpen]);
|
|||
|
|
|
|||
|
|
const validateForm = () => {
|
|||
|
|
const newErrors: Record<string, string> = {};
|
|||
|
|
|
|||
|
|
if (!formData.checkNumber.trim()) {
|
|||
|
|
newErrors.checkNumber = "Çek numarası gereklidir";
|
|||
|
|
}
|
|||
|
|
if (!formData.bankName.trim()) {
|
|||
|
|
newErrors.bankName = "Banka adı gereklidir";
|
|||
|
|
}
|
|||
|
|
if (!formData.drawerName.trim()) {
|
|||
|
|
newErrors.drawerName = "Keşideci adı gereklidir";
|
|||
|
|
}
|
|||
|
|
if (!formData.payeeName.trim()) {
|
|||
|
|
newErrors.payeeName = "Lehtar adı gereklidir";
|
|||
|
|
}
|
|||
|
|
if (formData.amount <= 0) {
|
|||
|
|
newErrors.amount = "Tutar 0'dan büyük olmalıdır";
|
|||
|
|
}
|
|||
|
|
if (new Date(formData.dueDate) < new Date(formData.issueDate)) {
|
|||
|
|
newErrors.dueDate = "Vade tarihi düzenleme tarihinden sonra olmalıdır";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setErrors(newErrors);
|
|||
|
|
return Object.keys(newErrors).length === 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
if (validateForm()) {
|
|||
|
|
const checkData = {
|
|||
|
|
...formData,
|
|||
|
|
issueDate: new Date(formData.issueDate),
|
|||
|
|
dueDate: new Date(formData.dueDate),
|
|||
|
|
currentAccount: formData.currentAccountId
|
|||
|
|
? currentAccounts.find((acc) => acc.id === formData.currentAccountId)
|
|||
|
|
: undefined,
|
|||
|
|
};
|
|||
|
|
onSave(checkData);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleInputChange = (
|
|||
|
|
field: string,
|
|||
|
|
value: string | number | CheckTypeEnum | CheckStatusEnum
|
|||
|
|
) => {
|
|||
|
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
|||
|
|
if (errors[field]) {
|
|||
|
|
setErrors((prev) => ({ ...prev, [field]: "" }));
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (!isOpen) return null;
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|||
|
|
<div className="bg-white rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
|||
|
|
<div className="flex items-center justify-between p-4 border-b">
|
|||
|
|
<div className="flex items-center gap-2.5">
|
|||
|
|
<FaCreditCard className="w-5 h-5 text-blue-600" />
|
|||
|
|
<h2 className="text-lg font-semibold text-gray-900">
|
|||
|
|
{check ? "Çek Düzenle" : "Yeni Çek"}
|
|||
|
|
</h2>
|
|||
|
|
</div>
|
|||
|
|
<button
|
|||
|
|
onClick={onCancel}
|
|||
|
|
className="p-2 hover:bg-gray-100 rounded-md"
|
|||
|
|
>
|
|||
|
|
<FaTimes className="w-5 h-5 text-gray-500" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<form onSubmit={handleSubmit} className="p-4 space-y-4">
|
|||
|
|
{/* Çek Bilgileri */}
|
|||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Çek Numarası *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={formData.checkNumber}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("checkNumber", e.target.value)
|
|||
|
|
}
|
|||
|
|
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|||
|
|
errors.checkNumber ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
placeholder="Çek numarasını giriniz"
|
|||
|
|
/>
|
|||
|
|
{errors.checkNumber && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">
|
|||
|
|
{errors.checkNumber}
|
|||
|
|
</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Tür *
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
value={formData.type}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("type", e.target.value as CheckTypeEnum)
|
|||
|
|
}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
>
|
|||
|
|
<option value={CheckTypeEnum.Received}>Alınan Çek</option>
|
|||
|
|
<option value={CheckTypeEnum.Issued}>Verilen Çek</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Durum
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
value={formData.status}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("status", e.target.value as CheckStatusEnum)
|
|||
|
|
}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
>
|
|||
|
|
<option value={CheckStatusEnum.InHand}>Elde</option>
|
|||
|
|
<option value={CheckStatusEnum.Deposited}>
|
|||
|
|
Bankaya Verildi
|
|||
|
|
</option>
|
|||
|
|
<option value={CheckStatusEnum.Collected}>Tahsil Edildi</option>
|
|||
|
|
<option value={CheckStatusEnum.Bounced}>Karşılıksız</option>
|
|||
|
|
<option value={CheckStatusEnum.Endorsed}>Ciro Edildi</option>
|
|||
|
|
<option value={CheckStatusEnum.Cancelled}>İptal</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Banka Bilgileri */}
|
|||
|
|
<div className="bg-gray-50 p-3 rounded-lg">
|
|||
|
|
<h3 className="text-base font-medium text-gray-900 mb-3">
|
|||
|
|
Banka Bilgileri
|
|||
|
|
</h3>
|
|||
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Banka Adı *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={formData.bankName}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("bankName", e.target.value)
|
|||
|
|
}
|
|||
|
|
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|||
|
|
errors.bankName ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
placeholder="Banka adını giriniz"
|
|||
|
|
/>
|
|||
|
|
{errors.bankName && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">{errors.bankName}</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Şube Adı
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={formData.branchName}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("branchName", e.target.value)
|
|||
|
|
}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
placeholder="Şube adını giriniz"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Hesap Numarası
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={formData.accountNumber}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("accountNumber", e.target.value)
|
|||
|
|
}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
placeholder="Hesap numarasını giriniz"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Taraf Bilgileri */}
|
|||
|
|
<div className="bg-gray-50 p-3 rounded-lg">
|
|||
|
|
<h3 className="text-base font-medium text-gray-900 mb-3">
|
|||
|
|
Taraf Bilgileri
|
|||
|
|
</h3>
|
|||
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Keşideci *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={formData.drawerName}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("drawerName", e.target.value)
|
|||
|
|
}
|
|||
|
|
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|||
|
|
errors.drawerName ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
placeholder="Keşideci adını giriniz"
|
|||
|
|
/>
|
|||
|
|
{errors.drawerName && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">
|
|||
|
|
{errors.drawerName}
|
|||
|
|
</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Lehtar *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
value={formData.payeeName}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("payeeName", e.target.value)
|
|||
|
|
}
|
|||
|
|
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|||
|
|
errors.payeeName ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
placeholder="Lehtar adını giriniz"
|
|||
|
|
/>
|
|||
|
|
{errors.payeeName && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">
|
|||
|
|
{errors.payeeName}
|
|||
|
|
</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Cari Hesap
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
value={formData.currentAccountId}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("currentAccountId", e.target.value)
|
|||
|
|
}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
>
|
|||
|
|
<option value="">Cari hesap seçiniz</option>
|
|||
|
|
{currentAccounts.map((account) => (
|
|||
|
|
<option key={account.id} value={account.id}>
|
|||
|
|
{account.accountCode} - {account.businessParty?.name}
|
|||
|
|
</option>
|
|||
|
|
))}
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Tutar ve Tarih Bilgileri */}
|
|||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Tutar *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="number"
|
|||
|
|
step="0.01"
|
|||
|
|
min="0"
|
|||
|
|
value={formData.amount}
|
|||
|
|
onChange={(e) =>
|
|||
|
|
handleInputChange("amount", parseFloat(e.target.value) || 0)
|
|||
|
|
}
|
|||
|
|
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|||
|
|
errors.amount ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
placeholder="0.00"
|
|||
|
|
/>
|
|||
|
|
{errors.amount && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">{errors.amount}</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Para Birimi
|
|||
|
|
</label>
|
|||
|
|
<select
|
|||
|
|
value={formData.currency}
|
|||
|
|
onChange={(e) => handleInputChange("currency", e.target.value)}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
>
|
|||
|
|
<option value="TRY">TRY</option>
|
|||
|
|
<option value="USD">USD</option>
|
|||
|
|
<option value="EUR">EUR</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Düzenleme Tarihi *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="date"
|
|||
|
|
value={formData.issueDate}
|
|||
|
|
onChange={(e) => handleInputChange("issueDate", e.target.value)}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Vade Tarihi *
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="date"
|
|||
|
|
value={formData.dueDate}
|
|||
|
|
onChange={(e) => handleInputChange("dueDate", e.target.value)}
|
|||
|
|
className={`w-full px-3 py-1.5 text-sm border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
|||
|
|
errors.dueDate ? "border-red-500" : "border-gray-300"
|
|||
|
|
}`}
|
|||
|
|
/>
|
|||
|
|
{errors.dueDate && (
|
|||
|
|
<p className="text-red-500 text-xs mt-1">{errors.dueDate}</p>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Notlar */}
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|||
|
|
Notlar
|
|||
|
|
</label>
|
|||
|
|
<textarea
|
|||
|
|
value={formData.notes}
|
|||
|
|
onChange={(e) => handleInputChange("notes", e.target.value)}
|
|||
|
|
rows={3}
|
|||
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
placeholder="Ek notlar..."
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Butonlar */}
|
|||
|
|
<div className="flex justify-end gap-3 pt-4 border-t">
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
onClick={onCancel}
|
|||
|
|
className="px-4 py-1.5 text-sm border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50 transition-colors"
|
|||
|
|
>
|
|||
|
|
İptal
|
|||
|
|
</button>
|
|||
|
|
<button
|
|||
|
|
type="submit"
|
|||
|
|
className="flex items-center gap-2 px-4 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
|||
|
|
>
|
|||
|
|
<FaSave className="w-4 h-4" />
|
|||
|
|
{check ? "Güncelle" : "Kaydet"}
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default CheckForm;
|