feat(admin): D16 (b)(c) — Excel order-item report, document-confirm SMS, %name in bulk SMS

Finished from a parallel session that expired (owner: «بساز»).

- RPT-01: GET admin/report/order-items — legacy report/order_item's
  32-column Excel (order items, one row per unit, plus financial
  documents), RTL, Jalali dates. The «گزارش تجمیعی» screen gets
  «دریافت فایل اکسل». Review fixes:
  - weight: computedWeightGrams is already one unit's weight; legacy
    divided it by qty again, under-reporting «بدهکار» on multi-unit
    lines. Not repeated.
  - «درصد»: the final percent (wage − discount), as legacy.
  - dates: the end date covers its whole day; a bad date is a 400.
- SMS-01: confirming or rejecting a financial document texts the customer,
  as legacy CashController::toggleConfiremCash.
- SMS-02: bulk SMS substitutes %name per recipient and is actually sent
  through the configured gateway (D26 relay), no longer only logged.
- Docs: 01-ADMIN-MATRIX and D16-BC-GAP-SUMMARY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
s.esmaeili
2026-10-08 16:34:45 +03:30
co-authored by Claude Opus 5.5
parent fbe4f38e06
commit 2d74f53d0a
12 changed files with 1241 additions and 30 deletions
@@ -14,7 +14,11 @@
<DateInput v-model="filters.start_date" label="از تاریخ" />
<DateInput v-model="filters.end_date" label="تا تاریخ" />
<a-form-item label=" ">
<a-button type="primary" @click="load">اعمال</a-button>
<a-space>
<a-button type="primary" @click="load">اعمال</a-button>
<!-- legacy's only output: «دریافت گزارش», the 32-column Excel file -->
<a-button :loading="downloading" @click="downloadExcel">دریافت فایل اکسل</a-button>
</a-space>
</a-form-item>
</div>
</a-form>
@@ -46,6 +50,7 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue';
import { message } from 'ant-design-vue';
import DateInput from '../../components/input/DateInput.vue';
import { jdate } from '../../utils/filters';
import api from '../../api/client';
@@ -83,6 +88,27 @@ async function load() {
}
}
const downloading = ref(false);
/** Legacy getReport: the order items + financial documents of the range as Excel. */
async function downloadExcel() {
try {
downloading.value = true;
const { data } = await api.get('admin/report/order-items', { params: filters, responseType: 'blob' });
const url = URL.createObjectURL(data as Blob);
const a = document.createElement('a');
a.href = url;
a.download = `report-${filters.start_date ?? 'all'}-${filters.end_date ?? 'all'}.xlsx`;
a.click();
URL.revokeObjectURL(url);
} catch (e) {
console.log(e);
message.error('خطا در دریافت فایل');
} finally {
downloading.value = false;
}
}
onMounted(load);
</script>