Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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/create", "POST", "insert", routes.CreateOrderHandler(pgDB, mssql)},
|
||||||
{"/api/order/update", "POST", "update", http.HandlerFunc(routes.UpdateOrderHandler)},
|
{"/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/order/get/{id}", "GET", "view", routes.GetOrderByIDHandler(mssql)},
|
||||||
{"/api/orders/list", "GET", "view", routes.OrderListRoute(mssql)},
|
{"/api/orders/list", "GET", "view", routes.OrderListRoute(mssql)},
|
||||||
{"/api/orders/production-list", "GET", "update", routes.OrderProductionListRoute(mssql)},
|
{"/api/orders/production-list", "GET", "update", routes.OrderProductionListRoute(mssql)},
|
||||||
|
|||||||
@@ -14,6 +14,62 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"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
|
// POST /api/order/update
|
||||||
// ================================
|
// ================================
|
||||||
|
|||||||
@@ -262,6 +262,16 @@
|
|||||||
@click="openNewRowEditor"
|
@click="openNewRowEditor"
|
||||||
:disable="isClosedRow || isViewOnly || !canMutateRows"
|
: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
|
<q-btn
|
||||||
v-if="canSubmitOrder"
|
v-if="canSubmitOrder"
|
||||||
:label="isEditMode ? 'TÜMÜNÜ GÜNCELLE' : 'TÜMÜNÜ KAYDET'"
|
:label="isEditMode ? 'TÜMÜNÜ GÜNCELLE' : 'TÜMÜNÜ KAYDET'"
|
||||||
@@ -450,6 +460,41 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</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)
|
🔹 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)
|
const aktifPB = ref('USD') // Varsayılan para birimi (Cari seçimiyle değişebilir)
|
||||||
// 🔹 Model detayları cache (product-detail API verilerini tutar)
|
// 🔹 Model detayları cache (product-detail API verilerini tutar)
|
||||||
const productCache = reactive({})
|
const productCache = reactive({})
|
||||||
|
const showBulkDueDateDialog = ref(false)
|
||||||
|
const bulkDueDateValue = ref('')
|
||||||
const confirmAndSubmit = async () => {
|
const confirmAndSubmit = async () => {
|
||||||
if (orderStore.loading) return
|
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
|
🗓️ SİPARİŞ TARİHLERİ — Varsayılan Değerler
|
||||||
@@ -939,6 +1023,14 @@ const canMutateRows = computed(() => {
|
|||||||
if (isViewOnly.value) return false
|
if (isViewOnly.value) return false
|
||||||
return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
|
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) {
|
function notifyNoPermission(message) {
|
||||||
$q.notify({
|
$q.notify({
|
||||||
|
|||||||
@@ -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) {
|
async downloadOrderPdf(id = null) {
|
||||||
try {
|
try {
|
||||||
const orderId = id || this.header?.OrderHeaderID
|
const orderId = id || this.header?.OrderHeaderID
|
||||||
@@ -3216,6 +3287,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
|
|||||||
if (!serverOrderId) {
|
if (!serverOrderId) {
|
||||||
throw new Error('OrderHeaderID backend’den dönmedi')
|
throw new Error('OrderHeaderID backend’den dönmedi')
|
||||||
}
|
}
|
||||||
|
const mailPayload = this.buildOrderMailPayload(lines, isNew)
|
||||||
purgeNewDraftOnExit = 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)
|
// 📧 Piyasa eşleşen alıcılara sipariş PDF gönderimi (kayıt başarılı olduktan sonra)
|
||||||
try {
|
try {
|
||||||
const mailPayload = this.buildOrderMailPayload(lines, isNew)
|
|
||||||
// UPDATE durumunda da mail gönderimi istendiği için isNew kontrolü kaldırıldı (v3.5)
|
// 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 mailRes = await this.sendOrderToMarketMails(serverOrderId, mailPayload)
|
||||||
const sentCount = Number(mailRes?.sentCount || 0)
|
const sentCount = Number(mailRes?.sentCount || 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user