Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-04-14 16:34:25 +03:00
parent 214677da1e
commit 47fc7a6178
4 changed files with 221 additions and 1 deletions

View File

@@ -519,6 +519,7 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
}{
{"/api/order/create", "POST", "insert", routes.CreateOrderHandler(pgDB, mssql)},
{"/api/order/update", "POST", "update", http.HandlerFunc(routes.UpdateOrderHandler)},
{"/api/order/{id}/bulk-due-date", "POST", "update", routes.BulkUpdateOrderLineDueDateHandler(mssql)},
{"/api/order/get/{id}", "GET", "view", routes.GetOrderByIDHandler(mssql)},
{"/api/orders/list", "GET", "view", routes.OrderListRoute(mssql)},
{"/api/orders/production-list", "GET", "update", routes.OrderProductionListRoute(mssql)},

View File

@@ -14,6 +14,62 @@ import (
"github.com/gorilla/mux"
)
func BulkUpdateOrderLineDueDateHandler(mssql *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
user := utils.UserFromClaims(claims)
if user == nil {
http.Error(w, "Kullanici dogrulanamadi", http.StatusUnauthorized)
return
}
orderHeaderID := mux.Vars(r)["id"]
if orderHeaderID == "" {
http.Error(w, "OrderHeaderID bulunamadi", http.StatusBadRequest)
return
}
var payload struct {
DueDate string `json:"dueDate"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
return
}
username := user.Username
if username == "" {
username = user.V3Username
}
updatedLines, headerUpdated, err := queries.BulkUpdateOrderLineDueDate(mssql, orderHeaderID, payload.DueDate, username)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(map[string]any{
"code": "ORDER_BULK_DUE_DATE_UPDATE_FAILED",
"message": "Siparis satir terminleri guncellenemedi.",
"detail": err.Error(),
})
return
}
_ = json.NewEncoder(w).Encode(map[string]any{
"success": true,
"orderHeaderID": orderHeaderID,
"dueDate": payload.DueDate,
"updatedLines": updatedLines,
"headerUpdated": headerUpdated,
})
}
}
// ================================
// POST /api/order/update
// ================================

View File

@@ -262,6 +262,16 @@
@click="openNewRowEditor"
:disable="isClosedRow || isViewOnly || !canMutateRows"
/>
<q-btn
v-if="isEditMode && canBulkUpdateLineDueDates"
label="SATIR TERMINLERINI TOPLU GUNCELLE"
color="warning"
icon="event"
class="q-ml-sm"
:loading="orderStore.loading"
:disable="orderStore.loading || !canBulkUpdateLineDueDates"
@click="openBulkDueDateDialog"
/>
<q-btn
v-if="canSubmitOrder"
:label="isEditMode ? 'TÜMÜNÜ GÜNCELLE' : 'TÜMÜNÜ KAYDET'"
@@ -450,6 +460,41 @@
</div>
</template>
</div>
<!-- =======================================================
🔹 TOPLU TERMIN GUNCELLEME
======================================================== -->
<q-dialog v-model="showBulkDueDateDialog" persistent>
<q-card style="min-width: 420px; max-width: 90vw;">
<q-card-section class="text-subtitle1 text-weight-bold">
Satir Terminlerini Toplu Guncelle
</q-card-section>
<q-card-section class="q-pt-none">
<div class="q-mb-md">
Tum siparis satiri terminlerini sectiginiz tarihi koyarak guncellemek istediginize emin misiniz?
</div>
<q-input
v-model="bulkDueDateValue"
type="date"
label="Yeni Termin Tarihi"
filled
dense
autofocus
/>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Iptal" v-close-popup />
<q-btn
color="primary"
label="Evet"
:loading="orderStore.loading"
@click="confirmBulkDueDateUpdate"
/>
</q-card-actions>
</q-card>
</q-dialog>
<!-- =======================================================
🔹 SATIR DÜZENLEYİCİ FORM (EDITOR)
======================================================== -->
@@ -883,6 +928,8 @@ console.log('🧩 Route parametresi alındı (setup başında):', orderHeaderID.
const aktifPB = ref('USD') // Varsayılan para birimi (Cari seçimiyle değişebilir)
// 🔹 Model detayları cache (product-detail API verilerini tutar)
const productCache = reactive({})
const showBulkDueDateDialog = ref(false)
const bulkDueDateValue = ref('')
const confirmAndSubmit = async () => {
if (orderStore.loading) return
@@ -918,6 +965,43 @@ const confirmAndSubmit = async () => {
}
}
function openBulkDueDateDialog() {
if (!canBulkUpdateLineDueDates.value) return
const firstRowDate = summaryRows.value?.find?.(row => !!row?.terminTarihi)?.terminTarihi || ''
bulkDueDateValue.value = toDateOnly(form.AverageDueDate || firstRowDate || dayjs().format('YYYY-MM-DD'))
showBulkDueDateDialog.value = true
}
async function confirmBulkDueDateUpdate() {
const dueDate = toDateOnly(bulkDueDateValue.value)
if (!dueDate) {
$q.notify({
type: 'warning',
message: 'Lutfen bir termin tarihi seciniz.'
})
return
}
try {
const result = await orderStore.bulkUpdateOrderLineDueDate(orderHeaderID.value, dueDate)
orderStore.applyBulkLineDueDateLocally(dueDate)
form.AverageDueDate = dueDate
showBulkDueDateDialog.value = false
$q.notify({
type: 'positive',
message: `Tum siparis satiri terminleri guncellendi (${Number(result?.updatedLines || 0)} satir).`
})
} catch (err) {
console.error('❌ confirmBulkDueDateUpdate hata:', err)
$q.notify({
type: 'negative',
message: err?.message || 'Satir terminleri guncellenemedi.'
})
}
}
/* ===========================================================
🗓️ SİPARİŞ TARİHLERİ — Varsayılan Değerler
@@ -939,6 +1023,14 @@ const canMutateRows = computed(() => {
if (isViewOnly.value) return false
return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
})
const canBulkUpdateLineDueDates = computed(() => {
if (!isEditMode.value) return false
if (isViewOnly.value) return false
if (isClosedOrder.value) return false
if (!canUpdateOrder.value) return false
if (!orderHeaderID.value) return false
return Array.isArray(orderStore.summaryRows) && orderStore.summaryRows.length > 0
})
function notifyNoPermission(message) {
$q.notify({

View File

@@ -668,6 +668,77 @@ export const useOrderEntryStore = defineStore('orderentry', {
}
,
async bulkUpdateOrderLineDueDate(orderId, dueDate) {
const id = String(orderId || this.header?.OrderHeaderID || '').trim()
const dateText = String(dueDate || '').trim()
if (!id) {
throw new Error('Siparis ID bulunamadi')
}
if (!dateText) {
throw new Error('Termin tarihi secilmedi')
}
try {
this.loading = true
const res = await api.post(`/order/${encodeURIComponent(id)}/bulk-due-date`, {
dueDate: dateText
})
return res?.data || {}
} catch (err) {
const detail = await extractApiErrorDetail(err)
const status = err?.status || err?.response?.status || '-'
console.error(`❌ bulkUpdateOrderLineDueDate hata [${status}] order=${id}: ${detail}`)
throw new Error(detail)
} finally {
this.loading = false
}
}
,
applyBulkLineDueDateLocally(dueDate) {
const dateText = String(dueDate || '').trim()
if (!dateText) return
const hadUnsavedChanges = this.hasUnsavedChanges
const patchRow = (row) => ({
...row,
terminTarihi: dateText,
DueDate: dateText,
DeliveryDate: dateText,
PlannedDateOfLading: dateText
})
this.orders = Array.isArray(this.orders)
? this.orders.map(patchRow)
: []
this.summaryRows = Array.isArray(this.summaryRows)
? this.summaryRows.map(patchRow)
: []
this.header = {
...(this.header || {}),
AverageDueDate: dateText
}
if (this.originalHeader && typeof this.originalHeader === 'object') {
this.originalHeader = {
...this.originalHeader,
AverageDueDate: dateText
}
}
if (Array.isArray(this.originalLines)) {
this.originalLines = this.originalLines.map(patchRow)
}
this.persistLocalStorage?.()
if (!hadUnsavedChanges) {
this.markAsSaved?.()
}
}
,
async downloadOrderPdf(id = null) {
try {
const orderId = id || this.header?.OrderHeaderID
@@ -3216,6 +3287,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
if (!serverOrderId) {
throw new Error('OrderHeaderID backendden dönmedi')
}
const mailPayload = this.buildOrderMailPayload(lines, isNew)
purgeNewDraftOnExit = isNew
/* =======================================================
@@ -3275,7 +3347,6 @@ export const useOrderEntryStore = defineStore('orderentry', {
// 📧 Piyasa eşleşen alıcılara sipariş PDF gönderimi (kayıt başarılı olduktan sonra)
try {
const mailPayload = this.buildOrderMailPayload(lines, isNew)
// UPDATE durumunda da mail gönderimi istendiği için isNew kontrolü kaldırıldı (v3.5)
const mailRes = await this.sendOrderToMarketMails(serverOrderId, mailPayload)
const sentCount = Number(mailRes?.sentCount || 0)