|
|
@ -36,8 +36,8 @@ func DocumentRemoveHandler(w http.ResponseWriter, r *http.Request) { |
|
|
"DocumentTypes": []map[string]string{ |
|
|
"DocumentTypes": []map[string]string{ |
|
|
{"value": "1", "label": "Job Paperwork"}, |
|
|
{"value": "1", "label": "Job Paperwork"}, |
|
|
{"value": "2", "label": "Job Vendor Bill"}, |
|
|
{"value": "2", "label": "Job Vendor Bill"}, |
|
|
{"value": "4", "label": "Generic Attachment"}, |
|
|
{"value": "7", "label": "Generic Attachment"}, |
|
|
{"value": "7", "label": "Blank Paperwork"}, |
|
|
{"value": "10", "label": "Blank Paperwork"}, |
|
|
{"value": "14", "label": "Job Invoice"}, |
|
|
{"value": "14", "label": "Job Invoice"}, |
|
|
}, |
|
|
}, |
|
|
} |
|
|
} |
|
|
@ -326,15 +326,17 @@ func RemoveJobAttachmentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Delete the attachment
|
|
|
// For all attachment types, we'll use the attachment endpoint for deletion
|
|
|
err = session.DeleteAttachment(id) |
|
|
// The API endpoint is /attachment/{id} as defined in DeleteAttachment method
|
|
|
|
|
|
log.Printf("Deleting attachment %s (ID: %s) using attachment endpoint", fileResult.Name, id) |
|
|
|
|
|
deleteErr := session.DeleteAttachment(id) |
|
|
|
|
|
|
|
|
mu.Lock() |
|
|
mu.Lock() |
|
|
defer mu.Unlock() |
|
|
defer mu.Unlock() |
|
|
|
|
|
|
|
|
if err != nil { |
|
|
if deleteErr != nil { |
|
|
fileResult.Success = false |
|
|
fileResult.Success = false |
|
|
fileResult.Error = err.Error() |
|
|
fileResult.Error = deleteErr.Error() |
|
|
results.ErrorCount++ |
|
|
results.ErrorCount++ |
|
|
results.Success = false |
|
|
results.Success = false |
|
|
} else { |
|
|
} else { |
|
|
@ -564,6 +566,271 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
|
|
|
// Only proceed if we have access or couldn't determine access
|
|
|
// Only proceed if we have access or couldn't determine access
|
|
|
if err != nil || hasAccess { |
|
|
if err != nil || hasAccess { |
|
|
|
|
|
// Try job paperwork endpoint
|
|
|
|
|
|
if stringInSlice("1", docTypes) || len(docTypes) == 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Trying the job paperwork endpoint", jobID) |
|
|
|
|
|
paperworkURL := fmt.Sprintf("%s/job/%s/paperwork", api.BaseURL, jobID) |
|
|
|
|
|
paperworkReq, err := http.NewRequest("GET", paperworkURL, nil) |
|
|
|
|
|
if err == nil { |
|
|
|
|
|
paperworkReq.Header.Set("Cookie", session.Cookie) |
|
|
|
|
|
paperworkReq.Header.Set("Accept", "application/json") |
|
|
|
|
|
|
|
|
|
|
|
log.Printf("**** JOB %s: Sending request to job paperwork endpoint: %s", jobID, paperworkURL) |
|
|
|
|
|
paperworkResp, err := session.Client.Do(paperworkReq) |
|
|
|
|
|
|
|
|
|
|
|
if err == nil && paperworkResp.StatusCode == http.StatusOK { |
|
|
|
|
|
defer paperworkResp.Body.Close() |
|
|
|
|
|
paperworkBody, _ := io.ReadAll(paperworkResp.Body) |
|
|
|
|
|
|
|
|
|
|
|
// Log preview of the response
|
|
|
|
|
|
responsePreview := string(paperworkBody) |
|
|
|
|
|
if len(responsePreview) > 300 { |
|
|
|
|
|
responsePreview = responsePreview[:300] + "... [truncated]" |
|
|
|
|
|
} |
|
|
|
|
|
log.Printf("**** JOB %s: Job paperwork response preview: %s", jobID, responsePreview) |
|
|
|
|
|
|
|
|
|
|
|
var paperworkResult map[string]interface{} |
|
|
|
|
|
if err := json.Unmarshal(paperworkBody, &paperworkResult); err == nil { |
|
|
|
|
|
// Process objects array if it exists
|
|
|
|
|
|
if objects, ok := paperworkResult["objects"].([]interface{}); ok && len(objects) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d paperwork items in objects array", jobID, len(objects)) |
|
|
|
|
|
|
|
|
|
|
|
for _, obj := range objects { |
|
|
|
|
|
if paperworkMap, ok := obj.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 1 for job paperwork
|
|
|
|
|
|
paperworkMap["purposeId"] = float64(1) |
|
|
|
|
|
attachments = append(attachments, paperworkMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added job paperwork to attachments", jobID) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else if data, ok := paperworkResult["data"].(map[string]interface{}); ok { |
|
|
|
|
|
// Check in data for attachments
|
|
|
|
|
|
if attachmentsArray, ok := data["attachments"].([]interface{}); ok && len(attachmentsArray) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d paperwork items in data.attachments", jobID, len(attachmentsArray)) |
|
|
|
|
|
|
|
|
|
|
|
for _, att := range attachmentsArray { |
|
|
|
|
|
if attMap, ok := att.(map[string]interface{}); ok { |
|
|
|
|
|
// Ensure purposeId is set correctly
|
|
|
|
|
|
attMap["purposeId"] = float64(1) |
|
|
|
|
|
attachments = append(attachments, attMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added job paperwork from data.attachments", jobID) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Also check other locations in data
|
|
|
|
|
|
possibleKeys := []string{"paperwork", "objects"} |
|
|
|
|
|
for _, key := range possibleKeys { |
|
|
|
|
|
if items, ok := data[key].([]interface{}); ok && len(items) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d paperwork items in data.%s", jobID, len(items), key) |
|
|
|
|
|
|
|
|
|
|
|
for _, item := range items { |
|
|
|
|
|
if itemMap, ok := item.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 1 for job paperwork
|
|
|
|
|
|
itemMap["purposeId"] = float64(1) |
|
|
|
|
|
attachments = append(attachments, itemMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added job paperwork from data.%s", jobID, key) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
log.Printf("**** JOB %s: Job paperwork endpoint failed or returned non-200 status: %v", jobID, err) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Try job invoice endpoint
|
|
|
|
|
|
if stringInSlice("14", docTypes) || len(docTypes) == 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Trying the job invoice endpoint", jobID) |
|
|
|
|
|
invoiceURL := fmt.Sprintf("%s/job/%s/invoice", api.BaseURL, jobID) |
|
|
|
|
|
invoiceReq, err := http.NewRequest("GET", invoiceURL, nil) |
|
|
|
|
|
if err == nil { |
|
|
|
|
|
invoiceReq.Header.Set("Cookie", session.Cookie) |
|
|
|
|
|
invoiceReq.Header.Set("Accept", "application/json") |
|
|
|
|
|
|
|
|
|
|
|
log.Printf("**** JOB %s: Sending request to job invoice endpoint: %s", jobID, invoiceURL) |
|
|
|
|
|
invoiceResp, err := session.Client.Do(invoiceReq) |
|
|
|
|
|
|
|
|
|
|
|
if err == nil && invoiceResp.StatusCode == http.StatusOK { |
|
|
|
|
|
defer invoiceResp.Body.Close() |
|
|
|
|
|
invoiceBody, _ := io.ReadAll(invoiceResp.Body) |
|
|
|
|
|
|
|
|
|
|
|
// Log preview of the response
|
|
|
|
|
|
responsePreview := string(invoiceBody) |
|
|
|
|
|
if len(responsePreview) > 300 { |
|
|
|
|
|
responsePreview = responsePreview[:300] + "... [truncated]" |
|
|
|
|
|
} |
|
|
|
|
|
log.Printf("**** JOB %s: Job invoice response preview: %s", jobID, responsePreview) |
|
|
|
|
|
|
|
|
|
|
|
var invoiceResult map[string]interface{} |
|
|
|
|
|
if err := json.Unmarshal(invoiceBody, &invoiceResult); err == nil { |
|
|
|
|
|
// Process objects array if it exists
|
|
|
|
|
|
if objects, ok := invoiceResult["objects"].([]interface{}); ok && len(objects) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d job invoices in objects array", jobID, len(objects)) |
|
|
|
|
|
|
|
|
|
|
|
for _, obj := range objects { |
|
|
|
|
|
if invoiceMap, ok := obj.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 14 for job invoices
|
|
|
|
|
|
invoiceMap["purposeId"] = float64(14) |
|
|
|
|
|
attachments = append(attachments, invoiceMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added job invoice to attachments", jobID) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else if data, ok := invoiceResult["data"].(map[string]interface{}); ok { |
|
|
|
|
|
// Check in data for attachments
|
|
|
|
|
|
possibleKeys := []string{"invoices", "attachments", "objects"} |
|
|
|
|
|
for _, key := range possibleKeys { |
|
|
|
|
|
if items, ok := data[key].([]interface{}); ok && len(items) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d invoices in data.%s", jobID, len(items), key) |
|
|
|
|
|
|
|
|
|
|
|
for _, item := range items { |
|
|
|
|
|
if itemMap, ok := item.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 14 for job invoices
|
|
|
|
|
|
itemMap["purposeId"] = float64(14) |
|
|
|
|
|
attachments = append(attachments, itemMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added job invoice from data.%s", jobID, key) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
log.Printf("**** JOB %s: Job invoice endpoint failed or returned non-200 status: %v", jobID, err) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Try generic attachment endpoint
|
|
|
|
|
|
if stringInSlice("7", docTypes) || len(docTypes) == 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Trying the generic attachment endpoint", jobID) |
|
|
|
|
|
genericURL := fmt.Sprintf("%s/job/%s/attachment", api.BaseURL, jobID) |
|
|
|
|
|
genericReq, err := http.NewRequest("GET", genericURL, nil) |
|
|
|
|
|
if err == nil { |
|
|
|
|
|
genericReq.Header.Set("Cookie", session.Cookie) |
|
|
|
|
|
genericReq.Header.Set("Accept", "application/json") |
|
|
|
|
|
|
|
|
|
|
|
log.Printf("**** JOB %s: Sending request to generic attachment endpoint: %s", jobID, genericURL) |
|
|
|
|
|
genericResp, err := session.Client.Do(genericReq) |
|
|
|
|
|
|
|
|
|
|
|
if err == nil && genericResp.StatusCode == http.StatusOK { |
|
|
|
|
|
defer genericResp.Body.Close() |
|
|
|
|
|
genericBody, _ := io.ReadAll(genericResp.Body) |
|
|
|
|
|
|
|
|
|
|
|
// Log preview of the response
|
|
|
|
|
|
responsePreview := string(genericBody) |
|
|
|
|
|
if len(responsePreview) > 300 { |
|
|
|
|
|
responsePreview = responsePreview[:300] + "... [truncated]" |
|
|
|
|
|
} |
|
|
|
|
|
log.Printf("**** JOB %s: Generic attachment response preview: %s", jobID, responsePreview) |
|
|
|
|
|
|
|
|
|
|
|
var genericResult map[string]interface{} |
|
|
|
|
|
if err := json.Unmarshal(genericBody, &genericResult); err == nil { |
|
|
|
|
|
// Process objects array if it exists
|
|
|
|
|
|
if objects, ok := genericResult["objects"].([]interface{}); ok && len(objects) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d generic attachments in objects array", jobID, len(objects)) |
|
|
|
|
|
|
|
|
|
|
|
for _, obj := range objects { |
|
|
|
|
|
if attachMap, ok := obj.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 7 for generic attachments
|
|
|
|
|
|
attachMap["purposeId"] = float64(7) |
|
|
|
|
|
attachments = append(attachments, attachMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added generic attachment to attachments", jobID) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else if data, ok := genericResult["data"].(map[string]interface{}); ok { |
|
|
|
|
|
// Check in data for attachments
|
|
|
|
|
|
possibleKeys := []string{"attachments", "objects"} |
|
|
|
|
|
for _, key := range possibleKeys { |
|
|
|
|
|
if items, ok := data[key].([]interface{}); ok && len(items) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d generic attachments in data.%s", jobID, len(items), key) |
|
|
|
|
|
|
|
|
|
|
|
for _, item := range items { |
|
|
|
|
|
if itemMap, ok := item.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 7 for generic attachments
|
|
|
|
|
|
itemMap["purposeId"] = float64(7) |
|
|
|
|
|
attachments = append(attachments, itemMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added generic attachment from data.%s", jobID, key) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
log.Printf("**** JOB %s: Generic attachment endpoint failed or returned non-200 status: %v", jobID, err) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Try vendor bill endpoint
|
|
|
|
|
|
if stringInSlice("2", docTypes) || len(docTypes) == 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Trying the vendor invoice endpoint", jobID) |
|
|
|
|
|
vendorInvoiceURL := fmt.Sprintf("%s/job/%s/vendorinvoice", api.BaseURL, jobID) |
|
|
|
|
|
vendorInvoiceReq, err := http.NewRequest("GET", vendorInvoiceURL, nil) |
|
|
|
|
|
if err == nil { |
|
|
|
|
|
vendorInvoiceReq.Header.Set("Cookie", session.Cookie) |
|
|
|
|
|
vendorInvoiceReq.Header.Set("Accept", "application/json") |
|
|
|
|
|
|
|
|
|
|
|
log.Printf("**** JOB %s: Sending request to vendor invoice endpoint: %s", jobID, vendorInvoiceURL) |
|
|
|
|
|
vendorInvoiceResp, err := session.Client.Do(vendorInvoiceReq) |
|
|
|
|
|
|
|
|
|
|
|
if err == nil && vendorInvoiceResp.StatusCode == http.StatusOK { |
|
|
|
|
|
defer vendorInvoiceResp.Body.Close() |
|
|
|
|
|
vendorInvoiceBody, _ := io.ReadAll(vendorInvoiceResp.Body) |
|
|
|
|
|
|
|
|
|
|
|
// Log preview of the response
|
|
|
|
|
|
responsePreview := string(vendorInvoiceBody) |
|
|
|
|
|
if len(responsePreview) > 300 { |
|
|
|
|
|
responsePreview = responsePreview[:300] + "... [truncated]" |
|
|
|
|
|
} |
|
|
|
|
|
log.Printf("**** JOB %s: Vendor invoice response preview: %s", jobID, responsePreview) |
|
|
|
|
|
|
|
|
|
|
|
var vendorInvoiceResult map[string]interface{} |
|
|
|
|
|
if err := json.Unmarshal(vendorInvoiceBody, &vendorInvoiceResult); err == nil { |
|
|
|
|
|
// Process objects array if it exists
|
|
|
|
|
|
if objects, ok := vendorInvoiceResult["objects"].([]interface{}); ok && len(objects) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d vendor invoices in objects array", jobID, len(objects)) |
|
|
|
|
|
|
|
|
|
|
|
for _, obj := range objects { |
|
|
|
|
|
if invoiceMap, ok := obj.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 2 for vendor bills
|
|
|
|
|
|
invoiceMap["purposeId"] = float64(2) |
|
|
|
|
|
attachments = append(attachments, invoiceMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added vendor invoice to attachments", jobID) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else if data, ok := vendorInvoiceResult["data"].(map[string]interface{}); ok { |
|
|
|
|
|
// Check in data for attachments
|
|
|
|
|
|
possibleKeys := []string{"invoices", "vendorInvoices", "attachments", "objects"} |
|
|
|
|
|
for _, key := range possibleKeys { |
|
|
|
|
|
if items, ok := data[key].([]interface{}); ok && len(items) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d vendor bills in data.%s", jobID, len(items), key) |
|
|
|
|
|
|
|
|
|
|
|
for _, item := range items { |
|
|
|
|
|
if itemMap, ok := item.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 2 for vendor bills
|
|
|
|
|
|
itemMap["purposeId"] = float64(2) |
|
|
|
|
|
attachments = append(attachments, itemMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added vendor bill from data.%s", jobID, key) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
log.Printf("**** JOB %s: Vendor invoice endpoint failed or returned non-200 status: %v", jobID, err) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Then continue with general paperwork endpoint to catch any we might have missed
|
|
|
|
|
|
log.Printf("**** JOB %s: Trying general paperwork endpoint", jobID) |
|
|
|
|
|
|
|
|
// Directly try to get paperwork using the specialized API
|
|
|
// Directly try to get paperwork using the specialized API
|
|
|
log.Printf("**** JOB %s: Using specialized paperwork API", jobID) |
|
|
log.Printf("**** JOB %s: Using specialized paperwork API", jobID) |
|
|
paperworkItems, err := session.GetJobPaperwork(jobID) |
|
|
paperworkItems, err := session.GetJobPaperwork(jobID) |
|
|
@ -648,7 +915,117 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
if len(attachments) == 0 { |
|
|
if len(attachments) == 0 { |
|
|
log.Printf("**** JOB %s: No attachments found yet, trying direct paperwork endpoint") |
|
|
log.Printf("**** JOB %s: No attachments found yet, trying direct paperwork endpoint") |
|
|
|
|
|
|
|
|
// Make a direct call to the paperwork endpoint
|
|
|
// Directly try to get vendor bills using a specific endpoint
|
|
|
|
|
|
log.Printf("**** JOB %s: Trying vendor bill specific endpoint", jobID) |
|
|
|
|
|
vendorBillURL := fmt.Sprintf("%s/job/%s/vendor-bill", api.BaseURL, jobID) |
|
|
|
|
|
vendorBillReq, err := http.NewRequest("GET", vendorBillURL, nil) |
|
|
|
|
|
if err == nil { |
|
|
|
|
|
vendorBillReq.Header.Set("Cookie", session.Cookie) |
|
|
|
|
|
vendorBillReq.Header.Set("Accept", "application/json") |
|
|
|
|
|
|
|
|
|
|
|
log.Printf("**** JOB %s: Sending request to vendor bill endpoint: %s", jobID, vendorBillURL) |
|
|
|
|
|
vendorBillResp, err := session.Client.Do(vendorBillReq) |
|
|
|
|
|
|
|
|
|
|
|
if err == nil && vendorBillResp.StatusCode == http.StatusOK { |
|
|
|
|
|
defer vendorBillResp.Body.Close() |
|
|
|
|
|
vendorBillBody, _ := io.ReadAll(vendorBillResp.Body) |
|
|
|
|
|
|
|
|
|
|
|
// Log full response structure for debugging
|
|
|
|
|
|
log.Printf("**** JOB %s: Full vendor bill response: %s", jobID, string(vendorBillBody)) |
|
|
|
|
|
|
|
|
|
|
|
var vendorBillResult map[string]interface{} |
|
|
|
|
|
if err := json.Unmarshal(vendorBillBody, &vendorBillResult); err == nil { |
|
|
|
|
|
// Log all root keys in the response
|
|
|
|
|
|
rootKeys := make([]string, 0) |
|
|
|
|
|
for k := range vendorBillResult { |
|
|
|
|
|
rootKeys = append(rootKeys, k) |
|
|
|
|
|
} |
|
|
|
|
|
log.Printf("**** JOB %s: Vendor bill response root keys: %s", |
|
|
|
|
|
jobID, strings.Join(rootKeys, ", ")) |
|
|
|
|
|
|
|
|
|
|
|
// Check if data exists and log all its keys
|
|
|
|
|
|
if data, ok := vendorBillResult["data"].(map[string]interface{}); ok { |
|
|
|
|
|
dataKeys := make([]string, 0) |
|
|
|
|
|
for k := range data { |
|
|
|
|
|
dataKeys = append(dataKeys, k) |
|
|
|
|
|
} |
|
|
|
|
|
log.Printf("**** JOB %s: Vendor bill data keys: %s", |
|
|
|
|
|
jobID, strings.Join(dataKeys, ", ")) |
|
|
|
|
|
|
|
|
|
|
|
// First try vendorBills directly
|
|
|
|
|
|
if vendorBills, ok := data["vendorBills"].([]interface{}); ok && len(vendorBills) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d vendor bills in data.vendorBills", jobID, len(vendorBills)) |
|
|
|
|
|
|
|
|
|
|
|
for _, bill := range vendorBills { |
|
|
|
|
|
if billMap, ok := bill.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 2 for vendor bills
|
|
|
|
|
|
billMap["purposeId"] = float64(2) |
|
|
|
|
|
attachments = append(attachments, billMap) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// Try other possible locations
|
|
|
|
|
|
log.Printf("**** JOB %s: No vendorBills found in data, checking other locations", jobID) |
|
|
|
|
|
|
|
|
|
|
|
// Try each possible location for the vendor bills
|
|
|
|
|
|
possibleKeys := []string{"objects", "attachments", "bills", "paperwork", "documents"} |
|
|
|
|
|
for _, key := range possibleKeys { |
|
|
|
|
|
if items, ok := data[key].([]interface{}); ok && len(items) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d items in data.%s", jobID, len(items), key) |
|
|
|
|
|
|
|
|
|
|
|
// Log the structure of the first item
|
|
|
|
|
|
if itemMap, ok := items[0].(map[string]interface{}); ok { |
|
|
|
|
|
itemKeys := make([]string, 0) |
|
|
|
|
|
for k := range itemMap { |
|
|
|
|
|
itemKeys = append(itemKeys, k) |
|
|
|
|
|
} |
|
|
|
|
|
log.Printf("**** JOB %s: First item in data.%s has keys: %s", |
|
|
|
|
|
jobID, key, strings.Join(itemKeys, ", ")) |
|
|
|
|
|
|
|
|
|
|
|
// Log the first item as JSON for inspection
|
|
|
|
|
|
if itemJSON, err := json.Marshal(itemMap); err == nil { |
|
|
|
|
|
log.Printf("**** JOB %s: First item in data.%s: %s", |
|
|
|
|
|
jobID, key, string(itemJSON)) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add all items as attachments
|
|
|
|
|
|
for _, item := range items { |
|
|
|
|
|
if itemMap, ok := item.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 2 for vendor bills
|
|
|
|
|
|
itemMap["purposeId"] = float64(2) |
|
|
|
|
|
attachments = append(attachments, itemMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added vendor bill from data.%s", jobID, key) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// If data is not a map, check for top-level objects
|
|
|
|
|
|
log.Printf("**** JOB %s: No data object in vendor bill response or it's not a map", jobID) |
|
|
|
|
|
|
|
|
|
|
|
if objects, ok := vendorBillResult["objects"].([]interface{}); ok && len(objects) > 0 { |
|
|
|
|
|
log.Printf("**** JOB %s: Found %d objects at root level", jobID, len(objects)) |
|
|
|
|
|
|
|
|
|
|
|
for _, obj := range objects { |
|
|
|
|
|
if objMap, ok := obj.(map[string]interface{}); ok { |
|
|
|
|
|
// Set purposeId to 2 for vendor bills
|
|
|
|
|
|
objMap["purposeId"] = float64(2) |
|
|
|
|
|
attachments = append(attachments, objMap) |
|
|
|
|
|
log.Printf("**** JOB %s: Added vendor bill from root.objects", jobID) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
log.Printf("**** JOB %s: Vendor bill endpoint failed or returned non-200 status: %v", jobID, err) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Also try direct paperwork endpoint
|
|
|
|
|
|
log.Printf("**** JOB %s: Trying direct paperwork endpoint", jobID) |
|
|
paperworkURL := fmt.Sprintf("%s/job/%s/paperwork", api.BaseURL, jobID) |
|
|
paperworkURL := fmt.Sprintf("%s/job/%s/paperwork", api.BaseURL, jobID) |
|
|
paperworkReq, err := http.NewRequest("GET", paperworkURL, nil) |
|
|
paperworkReq, err := http.NewRequest("GET", paperworkURL, nil) |
|
|
if err == nil { |
|
|
if err == nil { |
|
|
@ -720,6 +1097,14 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
// Log attachment count after direct endpoint
|
|
|
// Log attachment count after direct endpoint
|
|
|
log.Printf("**** JOB %s: Attachments found after direct paperwork call: %d", jobID, len(attachments)) |
|
|
log.Printf("**** JOB %s: Attachments found after direct paperwork call: %d", jobID, len(attachments)) |
|
|
|
|
|
|
|
|
|
|
|
// Deduplicate attachments to avoid processing the same ones multiple times
|
|
|
|
|
|
originalCount := len(attachments) |
|
|
|
|
|
attachments = deduplicateAttachments(attachments) |
|
|
|
|
|
if len(attachments) < originalCount { |
|
|
|
|
|
log.Printf("**** JOB %s: Removed %d duplicate attachments, %d unique attachments remain", |
|
|
|
|
|
jobID, originalCount-len(attachments), len(attachments)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Now actually apply the filters
|
|
|
// Now actually apply the filters
|
|
|
filteredAttachments := make([]map[string]interface{}, 0) |
|
|
filteredAttachments := make([]map[string]interface{}, 0) |
|
|
|
|
|
|
|
|
@ -800,14 +1185,17 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Get document name/filename
|
|
|
// Get filename from any available field, don't assume description exists
|
|
|
var filename string |
|
|
var filename string |
|
|
if nameVal, ok := attachment["fileName"].(string); ok { |
|
|
if desc, ok := attachment["description"].(string); ok && desc != "" { |
|
|
filename = nameVal |
|
|
filename = desc |
|
|
} else if nameVal, ok := attachment["name"].(string); ok { |
|
|
} else if name, ok := attachment["name"].(string); ok && name != "" { |
|
|
filename = nameVal |
|
|
filename = name |
|
|
} else if nameVal, ok := attachment["description"].(string); ok { |
|
|
} else if fname, ok := attachment["fileName"].(string); ok && fname != "" { |
|
|
filename = nameVal |
|
|
filename = fname |
|
|
|
|
|
} else { |
|
|
|
|
|
// If no name is available, use the ID as the name
|
|
|
|
|
|
filename = fmt.Sprintf("Attachment ID: %s", attachment["id"]) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
log.Printf("**** JOB %s: Attachment filename: %s", jobID, filename) |
|
|
log.Printf("**** JOB %s: Attachment filename: %s", jobID, filename) |
|
|
@ -876,6 +1264,14 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
|
|
|
if len(attachments) == 0 { |
|
|
if len(attachments) == 0 { |
|
|
log.Printf("**** JOB %s: WARNING! No attachments found after all retrieval attempts") |
|
|
log.Printf("**** JOB %s: WARNING! No attachments found after all retrieval attempts") |
|
|
|
|
|
} else { |
|
|
|
|
|
// Deduplicate again before continuing with deletion, in case multiple methods found the same attachments
|
|
|
|
|
|
originalCount := len(attachments) |
|
|
|
|
|
attachments = deduplicateAttachments(attachments) |
|
|
|
|
|
if len(attachments) < originalCount { |
|
|
|
|
|
log.Printf("**** JOB %s: Final deduplication removed %d duplicates, %d unique attachments remain", |
|
|
|
|
|
jobID, originalCount-len(attachments), len(attachments)) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -890,14 +1286,17 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
} |
|
|
} |
|
|
attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDRaw) |
|
|
attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDRaw) |
|
|
|
|
|
|
|
|
// Get the filename
|
|
|
// Safely get filename from any available field
|
|
|
var filename string |
|
|
var filename string |
|
|
if nameVal, ok := attachment["fileName"].(string); ok { |
|
|
if desc, ok := attachment["description"].(string); ok && desc != "" { |
|
|
filename = nameVal |
|
|
filename = desc |
|
|
} else if nameVal, ok := attachment["name"].(string); ok { |
|
|
} else if name, ok := attachment["name"].(string); ok && name != "" { |
|
|
filename = nameVal |
|
|
filename = name |
|
|
} else if nameVal, ok := attachment["description"].(string); ok { |
|
|
} else if fname, ok := attachment["fileName"].(string); ok && fname != "" { |
|
|
filename = nameVal |
|
|
filename = fname |
|
|
|
|
|
} else { |
|
|
|
|
|
// If no name is available, use the ID as the name
|
|
|
|
|
|
filename = fmt.Sprintf("Attachment ID: %s", attachmentIDStr) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if filename == "" { |
|
|
if filename == "" { |
|
|
@ -982,7 +1381,19 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
|
|
|
attachmentIDFloat := att["id"].(float64) |
|
|
attachmentIDFloat := att["id"].(float64) |
|
|
attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDFloat) // Convert to string without decimal
|
|
|
attachmentIDStr := fmt.Sprintf("%.0f", attachmentIDFloat) // Convert to string without decimal
|
|
|
filename := att["description"].(string) |
|
|
|
|
|
|
|
|
// Safely get filename from any available field
|
|
|
|
|
|
var filename string |
|
|
|
|
|
if desc, ok := att["description"].(string); ok && desc != "" { |
|
|
|
|
|
filename = desc |
|
|
|
|
|
} else if name, ok := att["name"].(string); ok && name != "" { |
|
|
|
|
|
filename = name |
|
|
|
|
|
} else if fname, ok := att["fileName"].(string); ok && fname != "" { |
|
|
|
|
|
filename = fname |
|
|
|
|
|
} else { |
|
|
|
|
|
// If no name is available, use the ID as the name
|
|
|
|
|
|
filename = fmt.Sprintf("Attachment ID: %s", attachmentIDStr) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
fileResult := struct { |
|
|
fileResult := struct { |
|
|
Name string |
|
|
Name string |
|
|
@ -992,16 +1403,18 @@ func BulkRemoveDocumentsHandler(w http.ResponseWriter, r *http.Request) { |
|
|
Name: filename, |
|
|
Name: filename, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Delete the attachment
|
|
|
// For all attachment types, we'll use the attachment endpoint for deletion
|
|
|
err := session.DeleteAttachment(attachmentIDStr) |
|
|
// The API endpoint is /attachment/{id} as defined in DeleteAttachment method
|
|
|
|
|
|
log.Printf("Deleting attachment %s (ID: %s) using attachment endpoint", filename, attachmentIDStr) |
|
|
|
|
|
deleteErr := session.DeleteAttachment(attachmentIDStr) |
|
|
|
|
|
|
|
|
mu.Lock() |
|
|
mu.Lock() |
|
|
defer mu.Unlock() |
|
|
defer mu.Unlock() |
|
|
|
|
|
|
|
|
if err != nil { |
|
|
if deleteErr != nil { |
|
|
fileResult.Success = false |
|
|
fileResult.Success = false |
|
|
fileResult.Error = err.Error() |
|
|
fileResult.Error = deleteErr.Error() |
|
|
log.Printf("Error deleting attachment %s: %v", filename, err) |
|
|
log.Printf("Error deleting attachment %s: %v", filename, deleteErr) |
|
|
jobResult.Success = false |
|
|
jobResult.Success = false |
|
|
} else { |
|
|
} else { |
|
|
fileResult.Success = true |
|
|
fileResult.Success = true |
|
|
@ -1174,3 +1587,31 @@ func logAttachmentDetails(jobID string, attachment map[string]interface{}) { |
|
|
} |
|
|
} |
|
|
log.Printf("***** END ATTACHMENT DETAILS *****") |
|
|
log.Printf("***** END ATTACHMENT DETAILS *****") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to deduplicate attachments based on ID
|
|
|
|
|
|
func deduplicateAttachments(attachments []map[string]interface{}) []map[string]interface{} { |
|
|
|
|
|
seen := make(map[string]bool) |
|
|
|
|
|
uniqueAttachments := make([]map[string]interface{}, 0) |
|
|
|
|
|
|
|
|
|
|
|
for _, attachment := range attachments { |
|
|
|
|
|
// Get the ID as a string for deduplication
|
|
|
|
|
|
var idStr string |
|
|
|
|
|
if id, ok := attachment["id"].(float64); ok { |
|
|
|
|
|
idStr = fmt.Sprintf("%.0f", id) |
|
|
|
|
|
} else if id, ok := attachment["id"].(string); ok { |
|
|
|
|
|
idStr = id |
|
|
|
|
|
} else { |
|
|
|
|
|
// If no valid ID, just add it (should not happen)
|
|
|
|
|
|
uniqueAttachments = append(uniqueAttachments, attachment) |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Only add if we haven't seen this ID before
|
|
|
|
|
|
if !seen[idStr] { |
|
|
|
|
|
seen[idStr] = true |
|
|
|
|
|
uniqueAttachments = append(uniqueAttachments, attachment) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return uniqueAttachments |
|
|
|
|
|
} |
|
|
|