const { Scenes, Markup } = require('telegraf'); const Product = require('../../models/Product'); const ExcelJS = require('exceljs'); const axios = require('axios'); const inventoryScene = new Scenes.WizardScene( 'admin_inventory', // Step 1: Menu async (ctx) => { // Inject i18n if (!ctx.i18n) { const User = require('../../models/User'); const locales = require('../../locales'); const user = await User.findOne({ id: ctx.from.id }); const lang = (user && user.language) ? user.language : 'uz'; ctx.i18n = locales[lang] || locales.uz; } const i18n = ctx.i18n; const text = i18n.admin.inv_title; const buttons = [ [Markup.button.callback(i18n.admin.inv_mass_price, "mass_price_update")], [Markup.button.callback(i18n.admin.inv_excel, "excel_import")], [Markup.button.callback(i18n.admin.back, "back_dashboard")] ]; if (ctx.callbackQuery) { await ctx.editMessageText(text, { parse_mode: 'Markdown', ...Markup.inlineKeyboard(buttons) }); } else { await ctx.replyWithMarkdown(text, Markup.inlineKeyboard(buttons)); } return ctx.wizard.next(); }, // Step 2: Handle Choice async (ctx) => { // Ensure i18n if (!ctx.i18n) { const locales = require('../../locales'); ctx.i18n = locales.uz; // Fallback } const i18n = ctx.i18n; if (!ctx.callbackQuery && !ctx.message) return; const txt = ctx.message ? ctx.message.text : ''; if (txt === '/start' || txt === i18n.admin.btn_reject || txt === 'āŒ Bekor qilish') return ctx.scene.leave(); if (ctx.callbackQuery) { const data = ctx.callbackQuery.data; if (data === 'back_dashboard') { await ctx.scene.leave(); const adminController = require('../../controllers/adminController'); // Re-fetch user logic inside controller usually handles i18n, but here we call it directly. // adminController uses ctx.i18n, so it's fine. return adminController.showDashboard(ctx); } if (data === 'mass_price_update') { await ctx.reply(i18n.admin.inv_price_prompt, { parse_mode: 'Markdown', ...Markup.keyboard([[i18n.admin.btn_reject]]).resize() }); ctx.wizard.state.action = 'mass_price'; return ctx.wizard.next(); } if (data === 'excel_import') { await ctx.reply(i18n.admin.inv_excel_prompt, Markup.keyboard([[i18n.admin.btn_reject]]).resize()); ctx.wizard.state.action = 'excel_import'; return ctx.wizard.next(); } } }, // Step 3: Execution async (ctx) => { // Ensure i18n if (!ctx.i18n) { const locales = require('../../locales'); ctx.i18n = locales.uz; } const i18n = ctx.i18n; const txt = ctx.message ? ctx.message.text : ''; if (txt === i18n.admin.btn_reject || txt === 'āŒ Bekor qilish') { await ctx.reply(i18n.admin.back || "Bekor qilindi.", Markup.removeKeyboard()); await ctx.scene.leave(); const adminController = require('../../controllers/adminController'); return adminController.showDashboard(ctx); } // --- Mass Price Update Logic --- if (ctx.wizard.state.action === 'mass_price') { const percent = parseInt(txt); if (isNaN(percent) || percent === 0) { await ctx.reply(i18n.admin.error_num || "Error"); return; } await ctx.reply(i18n.admin.broadcast_sending || "ā³..."); const products = await Product.find({}); let count = 0; for (let p of products) { try { if (!p.id || !p.name) continue; // Skip invalid const change = (p.price * percent) / 100; p.price = Math.round(p.price + change); await p.save(); count++; } catch (e) { console.error("Update error:", e.message); } } await ctx.reply(i18n.admin.inv_updated.replace('{count}', count), { parse_mode: 'Markdown', ...Markup.removeKeyboard() }); return ctx.scene.leave(); } // --- Excel Import Logic --- if (ctx.wizard.state.action === 'excel_import') { if (!ctx.message.document) { await ctx.reply(i18n.admin.error || "Error"); return; } const doc = ctx.message.document; if (!doc.file_name.endsWith('.xlsx')) { await ctx.reply("Only .xlsx"); return; } await ctx.reply(i18n.admin.broadcast_sending || "ā³..."); try { const fileLink = await ctx.telegram.getFileLink(doc.file_id); const response = await axios({ url: fileLink.href, method: 'GET', responseType: 'arraybuffer' }); const workbook = new ExcelJS.Workbook(); await workbook.xlsx.load(response.data); const worksheet = workbook.getWorksheet(1); // First sheet let imported = 0; let updated = 0; // Iterate rows (skip header) worksheet.eachRow(async (row, rowNumber) => { if (rowNumber === 1) return; // Header const id = row.getCell(1).value; const name = row.getCell(2).value; const price = row.getCell(3).value; const category = row.getCell(4).value; // logic: text or id? let's assume raw text/id const quantity = row.getCell(5).value; const description = row.getCell(6).value; if (id && name && price) { const existing = await Product.findOne({ id: id }); if (existing) { existing.name = name; existing.price = price; existing.category = category; existing.quantity = quantity || 0; existing.description = description || ""; await existing.save(); updated++; } else { const newProd = new Product({ id: id, name: name, price: price, category: category, quantity: quantity || 0, description: description || "" }); await newProd.save(); imported++; } } }); await ctx.reply(`${i18n.admin.inv_import_success}\nšŸ†•: ~${imported}\nšŸ”„: ~${updated}`, { parse_mode: 'Markdown', ...Markup.removeKeyboard() }); return ctx.scene.leave(); } catch (err) { console.error(err); await ctx.reply(i18n.admin.error); return ctx.scene.leave(); } } } ); module.exports = inventoryScene;