refactor(BillingManager): streamline next payment date calculation by integrating it into data normalization process
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m21s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m21s
This commit is contained in:
@@ -117,41 +117,6 @@ function BillingManager() {
|
|||||||
fetchServers();
|
fetchServers();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Пересчёт nextPaymentDate по платежам при загрузке (для уже сохранённых данных)
|
|
||||||
useEffect(() => {
|
|
||||||
if (billingData.length === 0 || ratesLoading) return;
|
|
||||||
setBillingData(prev => {
|
|
||||||
const updated = prev.map(item => {
|
|
||||||
const payments = item.payments || [];
|
|
||||||
if (payments.length === 0) return item;
|
|
||||||
const last = payments.slice().sort((a, b) => new Date(b.date) - new Date(a.date))[0];
|
|
||||||
const computed = (() => {
|
|
||||||
if (!last?.date) return null;
|
|
||||||
const amountRUB = (amt, ccy) => {
|
|
||||||
if (!amt) return 0;
|
|
||||||
const rub = Number(exchangeRates.RUB) || 1;
|
|
||||||
if (!ccy || ccy === 'RUB') return amt;
|
|
||||||
if (ccy === 'USD') return amt * rub;
|
|
||||||
const c = Number(exchangeRates[ccy]);
|
|
||||||
return c ? (amt / c) * rub : amt * rub;
|
|
||||||
};
|
|
||||||
const costRUB = amountRUB(item.monthlyCost || 0, item.monthlyCostCurrency || 'USD');
|
|
||||||
const amtRUB = amountRUB(last.amount, last.currency || 'USD');
|
|
||||||
const months = costRUB > 0 ? Math.max(1, Math.floor(amtRUB / costRUB)) : 1;
|
|
||||||
const d = new Date(last.date);
|
|
||||||
d.setMonth(d.getMonth() + months);
|
|
||||||
return d.toISOString().slice(0, 10);
|
|
||||||
})();
|
|
||||||
const current = new Date(item.nextPaymentDate || 0);
|
|
||||||
const now = new Date();
|
|
||||||
if (computed && current < now) return { ...item, nextPaymentDate: computed };
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
const changed = updated.some((it, i) => it.nextPaymentDate !== prev[i]?.nextPaymentDate);
|
|
||||||
return changed ? updated : prev;
|
|
||||||
});
|
|
||||||
}, [billingData.length, ratesLoading, exchangeRates]);
|
|
||||||
|
|
||||||
// Автоматическая синхронизация: добавляем серверы без записей в биллинге
|
// Автоматическая синхронизация: добавляем серверы без записей в биллинге
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (servers.length === 0 || loading) return;
|
if (servers.length === 0 || loading) return;
|
||||||
@@ -248,16 +213,40 @@ function BillingManager() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`/billing`);
|
const response = await api.get(`/billing`);
|
||||||
const normalized = (response.data || []).map((item, idx) => ({
|
const rates = exchangeRates; // из closure, на первой загрузке может быть {1,1,1} — для USD достаточно
|
||||||
id: item.id ?? String(idx + 1),
|
const toRUB = (amt, ccy) => {
|
||||||
payments: Array.isArray(item.payments)
|
if (!amt) return 0;
|
||||||
|
const rub = Number(rates.RUB) || 1;
|
||||||
|
if (!ccy || ccy === 'RUB') return amt;
|
||||||
|
if (ccy === 'USD') return amt * rub;
|
||||||
|
const c = Number(rates[ccy]);
|
||||||
|
return c ? (amt / c) * rub : amt * rub;
|
||||||
|
};
|
||||||
|
const normalized = (response.data || []).map((item, idx) => {
|
||||||
|
const payments = Array.isArray(item.payments)
|
||||||
? item.payments
|
? item.payments
|
||||||
: (item.lastPaymentDate && item.lastPaymentAmount
|
: (item.lastPaymentDate && item.lastPaymentAmount
|
||||||
? [{ date: item.lastPaymentDate, amount: item.lastPaymentAmount, currency: item.lastPaymentCurrency || 'USD', note: 'Импортировано' }]
|
? [{ date: item.lastPaymentDate, amount: item.lastPaymentAmount, currency: item.lastPaymentCurrency || 'USD', note: 'Импортировано' }]
|
||||||
: []
|
: []
|
||||||
),
|
);
|
||||||
...item,
|
const merged = { id: item.id ?? String(idx + 1), payments, ...item };
|
||||||
}));
|
if (payments.length > 0) {
|
||||||
|
const last = payments.slice().sort((a, b) => new Date(b.date) - new Date(a.date))[0];
|
||||||
|
if (last?.date) {
|
||||||
|
const current = new Date(item.nextPaymentDate || 0);
|
||||||
|
const now = new Date();
|
||||||
|
if (current < now || !item.nextPaymentDate) {
|
||||||
|
const costRUB = toRUB(item.monthlyCost || 0, item.monthlyCostCurrency || 'USD');
|
||||||
|
const amtRUB = toRUB(last.amount, last.currency || 'USD');
|
||||||
|
const months = costRUB > 0 ? Math.max(1, Math.floor(amtRUB / costRUB)) : 1;
|
||||||
|
const d = new Date(last.date);
|
||||||
|
d.setMonth(d.getMonth() + months);
|
||||||
|
merged.nextPaymentDate = d.toISOString().slice(0, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
});
|
||||||
setBillingData(normalized);
|
setBillingData(normalized);
|
||||||
setError('');
|
setError('');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user