diff --git a/taxes_az/client/company.js b/taxes_az/client/company.js
index 2011298..a015816 100644
--- a/taxes_az/client/company.js
+++ b/taxes_az/client/company.js
@@ -551,161 +551,332 @@ window.ETaxesCompany = {
});
},
- /**
- * Parse profile data and extract field mappings
- * ПОЛНАЯ ВЕРСИЯ: Включает все поля включая VAT и новые поля
- */
- _parseProfileData: function(profile, frm) {
- const updates = [];
-
- // Helper function to add update if value is different
- function addUpdate(fieldname, newValue, label, oldValue) {
- if (newValue && newValue !== oldValue && newValue.trim() !== '') {
- updates.push({
- fieldname: fieldname,
- newValue: newValue.trim(),
- oldValue: oldValue || '',
- label: label
- });
- }
- }
-
- // Helper function to format date from YYYYMMDD to YYYY-MM-DD
- function formatDate(dateStr) {
- if (!dateStr || dateStr.length !== 8) return null;
- const year = dateStr.substring(0, 4);
- const month = dateStr.substring(4, 6);
- const day = dateStr.substring(6, 8);
- return `${year}-${month}-${day}`;
- }
-
- // Tax ID mapping (TIN)
- if (profile.tin) {
- addUpdate('tax_id', profile.tin, __('Tax ID'), frm.doc.tax_id);
- }
-
- // Registration Number mapping
- if (profile.stateRegistrationDocumentNumber) {
- addUpdate('registration_details', profile.stateRegistrationDocumentNumber, __('Registration Details'), frm.doc.registration_details);
- }
-
- // Phone mapping
- const phoneNumber = ETaxesCompany._extractPhoneNumber(profile);
- if (phoneNumber) {
- addUpdate('phone_no', phoneNumber, __('Phone'), frm.doc.phone_no);
- }
-
- // Email mapping
- if (profile.email) {
- addUpdate('email', profile.email, __('Email'), frm.doc.email);
- }
-
- // Registration Date mapping
- if (profile.taxpayerRegistrationDate) {
- const regDate = moment(profile.taxpayerRegistrationDate).format('YYYY-MM-DD');
- addUpdate('date_of_establishment', regDate, __('Date of Establishment'), frm.doc.date_of_establishment);
- }
-
- // Domain mapping (if available)
- if (profile.mainActivity) {
- addUpdate('domain', profile.mainActivity, __('Domain'), frm.doc.domain);
- }
+ /**
+ * Parse profile data and extract field mappings
+ */
+ _parseProfileData: function(profile, frm) {
+ const updates = [];
+
+ // Helper function to add update if value is different
+ function addUpdate(fieldname, newValue, label, oldValue) {
+ // Специальная обработка для checkbox полей
+ if (typeof newValue === 'boolean' || fieldname.includes('is_') || fieldname.includes('has_') || fieldname.includes('special_tax_regime')) {
+ // Преобразуем оба значения к boolean для корректного сравнения
+ const newBool = Boolean(newValue);
+ const oldBool = Boolean(oldValue);
+
+ // Сравниваем как boolean
+ if (newBool !== oldBool) {
+ updates.push({
+ fieldname: fieldname,
+ newValue: newBool ? '1' : '0',
+ oldValue: oldBool ? '1' : '0',
+ label: label
+ });
+ }
+ } else {
+ // Для остальных полей - обычная логика
+ if (newValue !== null && newValue !== undefined && newValue !== oldValue) {
+ newValue = String(newValue).trim();
+ if (newValue !== '' && newValue !== String(oldValue || '').trim()) {
+ updates.push({
+ fieldname: fieldname,
+ newValue: newValue,
+ oldValue: oldValue || '',
+ label: label
+ });
+ }
+ }
+ }
+ }
+
+ // Helper function to format date from YYYYMMDD to YYYY-MM-DD
+ function formatDate(dateStr) {
+ if (!dateStr || dateStr.length !== 8) return null;
+ const year = dateStr.substring(0, 4);
+ const month = dateStr.substring(4, 6);
+ const day = dateStr.substring(6, 8);
+ return `${year}-${month}-${day}`;
+ }
+
+ // BASIC COMPANY INFORMATION
+ // Tax ID mapping (TIN)
+ if (profile.tin) {
+ addUpdate('tax_id', profile.tin, __('Tax ID'), frm.doc.tax_id);
+ }
+
+ // Registration Number mapping
+ if (profile.stateRegistrationDocumentNumber) {
+ addUpdate('registration_details', profile.stateRegistrationDocumentNumber, __('Registration Details'), frm.doc.registration_details);
+ }
+
+ // Phone mapping - prefer mobile over landline
+ const mobilePhone = profile.mobilePhoneNumber || ETaxesCompany._extractPhoneNumber(profile, 'mobile');
+ const landlinePhone = profile.landlinePhoneNumber || ETaxesCompany._extractPhoneNumber(profile, 'landline');
+
+ if (mobilePhone) {
+ addUpdate('phone_no', mobilePhone, __('Phone'), frm.doc.phone_no);
+ } else if (landlinePhone) {
+ addUpdate('phone_no', landlinePhone, __('Phone'), frm.doc.phone_no);
+ }
+
+ // Store both phones in dedicated fields
+ if (mobilePhone) {
+ addUpdate('mobile_phone', mobilePhone, __('Mobile Phone'), frm.doc.mobile_phone);
+ }
+ if (landlinePhone) {
+ addUpdate('landline_phone', landlinePhone, __('Landline Phone'), frm.doc.landline_phone);
+ }
+
+ // Email mapping
+ if (profile.email) {
+ addUpdate('email', profile.email, __('Email'), frm.doc.email);
+ }
+
+ // Registration Date mapping
+ if (profile.taxpayerRegistrationDate) {
+ const regDate = moment(profile.taxpayerRegistrationDate).format('YYYY-MM-DD');
+ addUpdate('date_of_establishment', regDate, __('Date of Establishment'), frm.doc.date_of_establishment);
+ }
+
+ // State Registration Document Issued Date
+ if (profile.stateRegistrationDocumentIssuedDate) {
+ const issuedDate = moment(profile.stateRegistrationDocumentIssuedDate).format('YYYY-MM-DD');
+ addUpdate('state_registration_document_issued_date', issuedDate, __('State Registration Document Issued Date'), frm.doc.state_registration_document_issued_date);
+ }
+
+ // Domain mapping (if available)
+ if (profile.mainActivity) {
+ addUpdate('domain', profile.mainActivity, __('Domain'), frm.doc.domain);
+ }
- // КАСТОМНЫЕ ПОЛЯ
-
- // Main Activity mapping (custom field)
- if (profile.mainActivity) {
- addUpdate('main_activity', profile.mainActivity, __('Main Activity'), frm.doc.main_activity);
- }
-
- // Taxation System mapping (custom field)
- if (profile.taxRate) {
- let taxationSystem = '';
- if (profile.taxRate === 'ƏDV' || profile.taxRate === 'VAT' || profile.taxRate === '18%') {
- taxationSystem = '18% Value-added tax';
- } else if (profile.taxRate === '0%' || profile.taxRate === 'ƏDV 0%') {
- taxationSystem = '0% Value-added tax';
- } else {
- taxationSystem = profile.taxRate; // Если не распознали, используем как есть
- }
- addUpdate('taxation_system', taxationSystem, __('Taxation System'), frm.doc.taxation_system);
- }
-
- // Business Classification mapping (custom field)
- if (profile.criteriaOfBusinessEntity) {
- let businessClassification = '';
-
- // Преобразование значений E-Taxes в правильные названия
- switch (profile.criteriaOfBusinessEntity.toLowerCase()) {
- case 'micro':
- businessClassification = 'Micro Entrepreneur';
- break;
- case 'small':
- businessClassification = 'Small Entrepreneur';
- break;
- case 'middle':
- case 'medium':
- businessClassification = 'Medium Entrepreneur';
- break;
- case 'big':
- case 'large':
- businessClassification = 'Big Entrepreneur';
- break;
- default:
- // Если не распознали, используем исходное значение с добавлением " Entrepreneur"
- businessClassification = profile.criteriaOfBusinessEntity + ' Entrepreneur';
- }
-
- addUpdate('business_classification', businessClassification, __('Business Classification'), frm.doc.business_classification);
- }
-
- // НОВЫЕ ПОЛЯ - добавляем пока коды, названия будут обновлены асинхронно
-
- // State Registration Authority
- if (profile.stateRegistrationName) {
- addUpdate('state_registration_authority', profile.stateRegistrationName, __('State Registration Authority'), frm.doc.state_registration_authority);
- }
-
- // Taxpayer Activity Group
- if (profile.activityGroup) {
- addUpdate('taxpayer_activity_group', profile.activityGroup, __('Taxpayer Activity Group'), frm.doc.taxpayer_activity_group);
- }
-
- // VAT ПОЛЯ
- if (profile.vatInfo && typeof profile.vatInfo === 'object') {
- // VAT Registration Date (prOperationTableOperationDate)
- if (profile.vatInfo.prOperationTableOperationDate) {
- const vatRegDate = formatDate(profile.vatInfo.prOperationTableOperationDate);
- if (vatRegDate) {
- addUpdate('vat_registration_date', vatRegDate, __('VAT Registration Date'), frm.doc.vat_registration_date);
- }
- }
-
- // VAT Certificate Number (recDocumentDocumentNumber)
- if (profile.vatInfo.recDocumentDocumentNumber) {
- addUpdate('vat_certificate_number', profile.vatInfo.recDocumentDocumentNumber, __('VAT Certificate Number'), frm.doc.vat_certificate_number);
- }
-
- // VAT Certificate Date (prVatOperationsRegDate)
- if (profile.vatInfo.prVatOperationsRegDate) {
- const vatCertDate = formatDate(profile.vatInfo.prVatOperationsRegDate);
- if (vatCertDate) {
- addUpdate('vat_certificate_date', vatCertDate, __('VAT Certificate Date'), frm.doc.vat_certificate_date);
- }
- }
- }
-
- // ОРГАНИЗАЦИОННАЯ СТРУКТУРА
-
- // Parent Organization (из headOrganizationName)
- if (profile.headOrganizationName && Array.isArray(profile.headOrganizationName) && profile.headOrganizationName.length > 0) {
- // Берем первую головную организацию или объединяем через запятую
- const parentOrgText = profile.headOrganizationName.join(', ');
- addUpdate('parent_organization', parentOrgText, __('Parent Organization'), frm.doc.parent_organization);
- }
-
- return updates;
- },
+ // MANAGEMENT INFORMATION
+ // Director Name from E-Taxes
+ if (profile.companyDirectorName) {
+ addUpdate('director_name_etaxes', profile.companyDirectorName, __('Director Name (E-Taxes)'), frm.doc.director_name_etaxes);
+ }
+
+ // Director PIN
+ if (profile.pin) {
+ addUpdate('director_pin', profile.pin, __('Director PIN'), frm.doc.director_pin);
+ }
+
+ // BUSINESS ACTIVITIES
+ // Main Activity mapping
+ if (profile.mainActivity) {
+ addUpdate('main_activity', profile.mainActivity, __('Main Activity'), frm.doc.main_activity);
+ }
+
+ // Legal Form Code
+ if (profile.legalFormCode) {
+ addUpdate('legal_form_code', profile.legalFormCode, __('Legal Form Code'), frm.doc.legal_form_code);
+ }
+
+ // TAX INFORMATION
+ // Taxation System mapping
+ if (profile.taxRate) {
+ let taxationSystem = '';
+ if (profile.taxRate === 'ƏDV' || profile.taxRate === 'VAT' || profile.taxRate === '18%') {
+ taxationSystem = '18% Value-added tax';
+ } else if (profile.taxRate === '0%' || profile.taxRate === 'ƏDV 0%') {
+ taxationSystem = '0% Value-added tax';
+ } else {
+ taxationSystem = profile.taxRate;
+ }
+ addUpdate('taxation_system', taxationSystem, __('Taxation System'), frm.doc.taxation_system);
+ }
+
+ // Business Classification mapping
+ if (profile.criteriaOfBusinessEntity) {
+ let businessClassification = '';
+
+ switch (profile.criteriaOfBusinessEntity.toLowerCase()) {
+ case 'micro':
+ businessClassification = 'Micro Entrepreneur';
+ break;
+ case 'small':
+ businessClassification = 'Small Entrepreneur';
+ break;
+ case 'middle':
+ case 'medium':
+ businessClassification = 'Medium Entrepreneur';
+ break;
+ case 'big':
+ case 'large':
+ businessClassification = 'Big Entrepreneur';
+ break;
+ default:
+ businessClassification = profile.criteriaOfBusinessEntity + ' Entrepreneur';
+ }
+
+ addUpdate('business_classification', businessClassification, __('Business Classification'), frm.doc.business_classification);
+ }
+
+ // State Registration Authority
+ if (profile.stateRegistrationName) {
+ addUpdate('state_registration_authority', profile.stateRegistrationName, __('State Registration Authority'), frm.doc.state_registration_authority);
+ }
+
+ // Taxpayer Activity Group
+ if (profile.activityGroup) {
+ addUpdate('taxpayer_activity_group', profile.activityGroup, __('Taxpayer Activity Group'), frm.doc.taxpayer_activity_group);
+ }
+
+ // Special Tax Regime
+ if (profile.specialTaxRegime !== undefined) {
+ addUpdate('special_tax_regime', profile.specialTaxRegime, __('Special Tax Regime'), frm.doc.special_tax_regime);
+ }
+
+ // TIN Type
+ if (profile.tinType) {
+ addUpdate('tin_type', profile.tinType, __('TIN Type'), frm.doc.tin_type);
+ }
+
+ // Is Risky
+ if (profile.isRisky !== undefined) {
+ addUpdate('is_risky_taxpayer', profile.isRisky, __('Is Risky Taxpayer'), frm.doc.is_risky_taxpayer);
+ }
+
+ // Sport Betting Operator
+ if (profile.sportBettingOperator !== undefined) {
+ addUpdate('is_sport_betting_operator', profile.sportBettingOperator, __('Sport Betting Operator'), frm.doc.is_sport_betting_operator);
+ }
+
+ // Has Active Production Object
+ if (profile.hasActiveProductionObject !== undefined) {
+ addUpdate('has_active_production_object', profile.hasActiveProductionObject, __('Has Active Production Object'), frm.doc.has_active_production_object);
+ }
+
+ // Tax System Type
+ if (profile.taxSystemType) {
+ addUpdate('tax_system_type', profile.taxSystemType, __('Tax System Type'), frm.doc.tax_system_type);
+ }
+
+ // Is Taxpayer in Cancellation Process
+ if (profile.isTaxPayerInCancellationProcess !== undefined) {
+ addUpdate('is_taxpayer_in_cancellation_process', profile.isTaxPayerInCancellationProcess, __('Is Taxpayer in Cancellation Process'), frm.doc.is_taxpayer_in_cancellation_process);
+ }
+
+ // Is Chief of Any Legal Entity
+ if (profile.isChiefOfAnyLegalEntity !== undefined) {
+ addUpdate('is_chief_of_any_legal_entity', profile.isChiefOfAnyLegalEntity, __('Is Chief of Any Legal Entity'), frm.doc.is_chief_of_any_legal_entity);
+ }
+
+ // VAT INFORMATION
+ if (profile.vatInfo && typeof profile.vatInfo === 'object') {
+ // VAT Registration Date
+ if (profile.vatInfo.prOperationTableOperationDate) {
+ const vatRegDate = formatDate(profile.vatInfo.prOperationTableOperationDate);
+ if (vatRegDate) {
+ addUpdate('vat_registration_date', vatRegDate, __('VAT Registration Date'), frm.doc.vat_registration_date);
+ }
+ }
+
+ // VAT Certificate Number
+ if (profile.vatInfo.recDocumentDocumentNumber) {
+ addUpdate('vat_certificate_number', profile.vatInfo.recDocumentDocumentNumber, __('VAT Certificate Number'), frm.doc.vat_certificate_number);
+ }
+
+ // VAT Certificate Date
+ if (profile.vatInfo.prVatOperationsRegDate) {
+ const vatCertDate = formatDate(profile.vatInfo.prVatOperationsRegDate);
+ if (vatCertDate) {
+ addUpdate('vat_certificate_date', vatCertDate, __('VAT Certificate Date'), frm.doc.vat_certificate_date);
+ }
+ }
+ }
+
+ // ORGANIZATIONAL STRUCTURE
+ // Parent Organization
+ if (profile.headOrganizationName && Array.isArray(profile.headOrganizationName) && profile.headOrganizationName.length > 0) {
+ const parentOrgText = profile.headOrganizationName.join(', ');
+ addUpdate('parent_organization', parentOrgText, __('Parent Organization'), frm.doc.parent_organization);
+ }
+
+ // Parent Organization TIN
+ if (profile.headOrganizationTin) {
+ addUpdate('parent_organization_tin', profile.headOrganizationTin, __('Parent Organization TIN'), frm.doc.parent_organization_tin);
+ }
+
+ // Employee Count
+ if (profile.employeeData && profile.employeeData.employeeCount !== undefined) {
+ addUpdate('employee_count', profile.employeeData.employeeCount, __('Employee Count'), frm.doc.employee_count);
+ }
+
+ // REGISTRATION DATES
+ // Suspension Dates
+ if (profile.suspensionStartDate) {
+ const suspStartDate = moment(profile.suspensionStartDate).format('YYYY-MM-DD');
+ addUpdate('suspension_start_date', suspStartDate, __('Suspension Start Date'), frm.doc.suspension_start_date);
+ }
+
+ if (profile.suspensionEndDate) {
+ const suspEndDate = moment(profile.suspensionEndDate).format('YYYY-MM-DD');
+ addUpdate('suspension_end_date', suspEndDate, __('Suspension End Date'), frm.doc.suspension_end_date);
+ }
+
+ // Liquidation Date
+ if (profile.liquidationDate) {
+ const liqDate = moment(profile.liquidationDate).format('YYYY-MM-DD');
+ addUpdate('liquidation_date', liqDate, __('Liquidation Date'), frm.doc.liquidation_date);
+ }
+
+ // ADDRESS INFORMATION
+ // Legal Address Full
+ if (profile.legalAddress) {
+ addUpdate('legal_address_full', profile.legalAddress, __('Legal Address (Full)'), frm.doc.legal_address_full);
+ }
+
+ // Actual Address Full
+ if (profile.actualAddress) {
+ addUpdate('actual_address_full', profile.actualAddress, __('Actual Address (Full)'), frm.doc.actual_address_full);
+ }
+
+ // Address for Mail
+ if (profile.addressForMail) {
+ addUpdate('address_for_mail', profile.addressForMail, __('Address for Mail'), frm.doc.address_for_mail);
+ }
+
+ // Legal Address Details
+ if (profile.legalAddressObject && typeof profile.legalAddressObject === 'object') {
+ if (profile.legalAddressObject.postcode) {
+ addUpdate('legal_address_postcode', profile.legalAddressObject.postcode, __('Legal Address Postcode'), frm.doc.legal_address_postcode);
+ }
+ if (profile.legalAddressObject.region) {
+ addUpdate('legal_address_region', profile.legalAddressObject.region, __('Legal Address Region'), frm.doc.legal_address_region);
+ }
+ if (profile.legalAddressObject.locality) {
+ addUpdate('legal_address_locality', profile.legalAddressObject.locality, __('Legal Address Locality'), frm.doc.legal_address_locality);
+ }
+ if (profile.legalAddressObject.street) {
+ addUpdate('legal_address_street', profile.legalAddressObject.street, __('Legal Address Street'), frm.doc.legal_address_street);
+ }
+ if (profile.legalAddressObject.houseNumber) {
+ addUpdate('legal_address_house', profile.legalAddressObject.houseNumber, __('Legal Address House Number'), frm.doc.legal_address_house);
+ }
+ if (profile.legalAddressObject.roomNumber) {
+ addUpdate('legal_address_room', profile.legalAddressObject.roomNumber, __('Legal Address Room Number'), frm.doc.legal_address_room);
+ }
+ }
+
+ // TAX SYSTEMS LIST
+ if (profile.taxSystems && Array.isArray(profile.taxSystems) && profile.taxSystems.length > 0) {
+ const taxSystemsList = profile.taxSystems.join(', ');
+ addUpdate('tax_systems_list', taxSystemsList, __('Tax Systems List'), frm.doc.tax_systems_list);
+ }
+
+ // EMPLOYER INFORMATION
+ if (profile.employer && typeof profile.employer === 'object') {
+ if (profile.employer.name) {
+ addUpdate('employer_name', profile.employer.name, __('Employer Name'), frm.doc.employer_name);
+ }
+ if (profile.employer.position) {
+ addUpdate('employer_position', profile.employer.position, __('Employer Position'), frm.doc.employer_position);
+ }
+ }
+
+ return updates;
+ },
/**
* НОВАЯ ФУНКЦИЯ: Обновляем поля с названиями из справочников
@@ -726,296 +897,370 @@ window.ETaxesCompany = {
}
},
- /**
- * ИСПРАВЛЕННАЯ ВЕРСИЯ: Обновляем Additional Activity Types из additionalActivityCodes
- * с проверкой существования в Main type of activity
- */
- _updateAdditionalActivities: function(profile, frm) {
- if (profile.additionalActivityCodes && Array.isArray(profile.additionalActivityCodes) && profile.additionalActivityCodes.length > 0) {
- // Очищаем существующие записи в табличной части Additional Activity Types
- frm.clear_table('additional_activity_types');
-
- let addedCount = 0;
- let skippedCount = 0;
- let processedCount = 0;
-
- // Функция для проверки и добавления одного кода активности
- function processActivityCode(activityCode, callback) {
- if (activityCode && activityCode.trim()) {
- const trimmedCode = activityCode.trim();
-
- // Проверяем существование записи Main type of activity перед добавлением
- frappe.call({
- method: 'frappe.client.get_value',
- args: {
- doctype: 'Main type of activity',
- fieldname: 'name',
- filters: {name: trimmedCode}
- },
- callback: function(r) {
- processedCount++;
-
- if (r.message && r.message.name) {
- // Запись существует - добавляем
- let row = frm.add_child('additional_activity_types');
- row.activity_type = trimmedCode;
- addedCount++;
- } else {
- // Запись не существует - пропускаем
- skippedCount++;
- }
-
- // Вызываем callback когда обработка завершена
- if (callback) callback();
- },
- error: function() {
- processedCount++;
- skippedCount++;
- if (callback) callback();
- }
- });
- } else {
- processedCount++;
- skippedCount++;
- if (callback) callback();
- }
- }
-
- // Функция для последовательной обработки всех кодов
- function processAllCodes(codes, index = 0) {
- if (index >= codes.length) {
- // Все коды обработаны - обновляем UI и показываем результат
- frm.refresh_field('additional_activity_types');
-
- let message = '';
- if (addedCount > 0) {
- message = __('Added {0} additional activities', [addedCount]);
- if (skippedCount > 0) {
- message += __(', skipped {0} (not found)', [skippedCount]);
- }
- frappe.show_alert({
- message: message,
- indicator: 'green'
- }, 4);
- } else {
- message = __('No valid additional activities found');
- if (skippedCount > 0) {
- message += __(', {0} codes not found in system', [skippedCount]);
- }
- frappe.show_alert({
- message: message,
- indicator: 'orange'
- }, 4);
- }
-
- return;
- }
-
- // Обрабатываем текущий код и переходим к следующему
- processActivityCode(codes[index], function() {
- processAllCodes(codes, index + 1);
- });
- }
-
- // Запускаем обработку всех кодов
- processAllCodes(profile.additionalActivityCodes);
-
- } else {
- frappe.show_alert({
- message: __('No additional activities to process'),
- indicator: 'blue'
- }, 3);
- }
- },
+ _updateAdditionalActivities: function(profile, frm) {
+ if (profile.additionalActivityCodes && Array.isArray(profile.additionalActivityCodes) && profile.additionalActivityCodes.length > 0) {
+ // ВАЖНО: Не очищаем таблицу сразу, а работаем с существующими данными
+ const existingActivities = frm.doc.additional_activity_types || [];
+ const existingCodes = existingActivities.map(row => row.activity_type);
+
+ let addedCount = 0;
+ let skippedCount = 0;
+ let duplicateCount = 0;
+
+ // Функция для проверки и добавления одного кода активности
+ function processActivityCode(activityCode, callback) {
+ if (activityCode && activityCode.trim()) {
+ const trimmedCode = activityCode.trim();
+
+ // Проверяем, не добавлен ли уже этот код
+ if (existingCodes.includes(trimmedCode)) {
+ duplicateCount++;
+ if (callback) callback();
+ return;
+ }
+
+ // Проверяем существование записи Main type of activity перед добавлением
+ frappe.call({
+ method: 'frappe.client.get_value',
+ args: {
+ doctype: 'Main type of activity',
+ fieldname: 'name',
+ filters: {name: trimmedCode}
+ },
+ callback: function(r) {
+ if (r.message && r.message.name) {
+ // Запись существует - добавляем
+ let row = frm.add_child('additional_activity_types');
+ row.activity_type = trimmedCode;
+ addedCount++;
+ } else {
+ // Запись не существует - пропускаем
+ skippedCount++;
+ }
+
+ if (callback) callback();
+ },
+ error: function() {
+ skippedCount++;
+ if (callback) callback();
+ }
+ });
+ } else {
+ skippedCount++;
+ if (callback) callback();
+ }
+ }
+
+ // Функция для последовательной обработки всех кодов
+ function processAllCodes(codes, index = 0) {
+ if (index >= codes.length) {
+ // Все коды обработаны - обновляем UI
+ frm.refresh_field('additional_activity_types');
+
+ let message = '';
+ if (addedCount > 0) {
+ message = __('Added {0} additional activities', [addedCount]);
+ if (skippedCount > 0) {
+ message += __(', skipped {0} (not found)', [skippedCount]);
+ }
+ if (duplicateCount > 0) {
+ message += __(', {0} already existed', [duplicateCount]);
+ }
+ frappe.show_alert({
+ message: message,
+ indicator: 'green'
+ }, 4);
+ } else if (duplicateCount > 0) {
+ message = __('All {0} activities already exist', [duplicateCount]);
+ frappe.show_alert({
+ message: message,
+ indicator: 'blue'
+ }, 4);
+ } else {
+ message = __('No valid additional activities found');
+ if (skippedCount > 0) {
+ message += __(', {0} codes not found in system', [skippedCount]);
+ }
+ frappe.show_alert({
+ message: message,
+ indicator: 'orange'
+ }, 4);
+ }
+
+ return;
+ }
+
+ // Обрабатываем текущий код и переходим к следующему
+ processActivityCode(codes[index], function() {
+ processAllCodes(codes, index + 1);
+ });
+ }
+
+ // Запускаем обработку всех кодов
+ processAllCodes(profile.additionalActivityCodes);
+
+ } else {
+ frappe.show_alert({
+ message: __('No additional activities to process'),
+ indicator: 'blue'
+ }, 3);
+ }
+ },
- /**
- * ОБНОВЛЕННАЯ ФУНКЦИЯ: Обновляем дочерние организации из affiliatesNames
- */
- _updateAffiliateOrganizations: function(profile, frm) {
- if (profile.affiliatesNames && Array.isArray(profile.affiliatesNames) && profile.affiliatesNames.length > 0) {
- // Очищаем существующие записи
- frm.clear_table('affiliate_organizations');
-
- let addedCount = 0;
-
- // Добавляем каждую дочернюю организацию из affiliatesNames
- profile.affiliatesNames.forEach(function(orgName, index) {
- if (orgName && orgName.trim()) {
- let row = frm.add_child('affiliate_organizations');
-
- // Имя заполняем из массива affiliatesNames
- row.organization_name = orgName.trim();
-
- addedCount++;
- }
- });
-
- // Обновляем отображение табличной части
- frm.refresh_field('affiliate_organizations');
-
- if (addedCount > 0) {
- frappe.show_alert({
- message: __('Added {0} affiliate organizations', [addedCount]),
- indicator: 'green'
- }, 3);
- }
- } else {
- // Очищаем таблицу если нет дочерних организаций
- frm.clear_table('affiliate_organizations');
- frm.refresh_field('affiliate_organizations');
- }
- },
+ _updateAffiliateOrganizations: function(profile, frm) {
+ // Проверяем наличие дочерних организаций
+ const hasAffiliateNames = profile.affiliatesNames && Array.isArray(profile.affiliatesNames) && profile.affiliatesNames.length > 0;
+ const hasAffiliateTins = profile.affiliatesTins && Array.isArray(profile.affiliatesTins) && profile.affiliatesTins.length > 0;
+
+ if (hasAffiliateNames || hasAffiliateTins) {
+ // Очищаем существующие записи в табличной части
+ frm.clear_table('affiliate_organizations');
+
+ let addedCount = 0;
+ let skippedCount = 0;
+
+ // Определяем максимальную длину массивов
+ const maxLength = Math.max(
+ hasAffiliateNames ? profile.affiliatesNames.length : 0,
+ hasAffiliateTins ? profile.affiliatesTins.length : 0
+ );
+
+ // Обрабатываем каждую организацию
+ for (let i = 0; i < maxLength; i++) {
+ // Получаем имя организации
+ let orgName = '';
+ if (hasAffiliateNames && profile.affiliatesNames[i]) {
+ orgName = profile.affiliatesNames[i].trim();
+ }
+
+ // Получаем TIN организации
+ let orgTin = '';
+ if (hasAffiliateTins && profile.affiliatesTins[i]) {
+ orgTin = profile.affiliatesTins[i].trim();
+ }
+
+ // Добавляем строку только если есть хотя бы имя или TIN
+ if (orgName || orgTin) {
+ let row = frm.add_child('affiliate_organizations');
+
+ // Заполняем имя организации
+ if (orgName) {
+ row.organization_name = orgName;
+ } else {
+ // Если нет имени, но есть TIN, используем TIN как имя
+ row.organization_name = `Organization (TIN: ${orgTin})`;
+ }
+
+ // Заполняем TIN если есть
+ if (orgTin) {
+ row.organization_tin = orgTin;
+ }
+
+ addedCount++;
+
+ // Логируем для отладки
+ console.log(`Added affiliate organization: ${row.organization_name} (TIN: ${row.organization_tin || 'N/A'})`);
+ } else {
+ skippedCount++;
+ console.log(`Skipped empty affiliate organization at index ${i}`);
+ }
+ }
+
+ // Обновляем отображение табличной части
+ frm.refresh_field('affiliate_organizations');
+
+ // Показываем результат
+ if (addedCount > 0) {
+ let message = __('Added {0} affiliate organization(s)', [addedCount]);
+ if (skippedCount > 0) {
+ message += __(', skipped {0} empty record(s)', [skippedCount]);
+ }
+
+ frappe.show_alert({
+ message: message,
+ indicator: 'green'
+ }, 4);
+
+ // Дополнительное логирование
+ console.log(`Affiliate organizations update completed: ${addedCount} added, ${skippedCount} skipped`);
+ } else {
+ frappe.show_alert({
+ message: __('No valid affiliate organizations found to add'),
+ indicator: 'orange'
+ }, 3);
+ }
+
+ } else {
+ // Нет дочерних организаций - очищаем таблицу
+ if (frm.doc.affiliate_organizations && frm.doc.affiliate_organizations.length > 0) {
+ frm.clear_table('affiliate_organizations');
+ frm.refresh_field('affiliate_organizations');
+
+ frappe.show_alert({
+ message: __('Cleared affiliate organizations (none found in E-Taxes)'),
+ indicator: 'blue'
+ }, 3);
+ }
+
+ console.log('No affiliate organizations found in profile data');
+ }
+
+ // Дополнительная проверка консистентности данных
+ if (hasAffiliateNames && hasAffiliateTins) {
+ if (profile.affiliatesNames.length !== profile.affiliatesTins.length) {
+ console.warn(`Warning: Mismatch in affiliate data - ${profile.affiliatesNames.length} names vs ${profile.affiliatesTins.length} TINs`);
+
+ // Можно показать предупреждение пользователю
+ frappe.show_alert({
+ message: __('Warning: Number of affiliate names and TINs do not match'),
+ indicator: 'yellow'
+ }, 5);
+ }
+ }
+ },
- /**
- * Update company fields from profile data with automatic parsing
- * ОБНОВЛЕННАЯ ВЕРСИЯ: с новыми полями и справочниками
- */
- updateCompanyFromProfile: function(profileData, frm) {
- try {
- const profile = profileData.profile || profileData;
- const updates = ETaxesCompany._parseProfileData(profile, frm);
-
- if (updates.length > 0) {
- // Show confirmation with list of changes
- let changesList = '
';
- updates.forEach(update => {
- changesList += `- ${update.label}: ${update.oldValue || __('(empty)')} → ${update.newValue}
`;
- });
- changesList += '
';
-
- // Добавляем информацию о дополнительных данных
- let additionalInfo = '';
- if (profile.additionalActivityCodes && profile.additionalActivityCodes.length > 0) {
- additionalInfo += `
${__('Additional activities to be added')}: ${profile.additionalActivityCodes.length} ${__('activities')}`;
- }
- if (profile.affiliatesNames && profile.affiliatesNames.length > 0) {
- additionalInfo += `
${__('Affiliate organizations to be added')}: ${profile.affiliatesNames.length} ${__('organizations')}`;
- }
-
- frappe.confirm(
- __('The following company fields will be updated with data from E-Taxes:') +
- '
' + changesList + additionalInfo +
- '
' + __('Continue?'),
- function() {
- // Apply all updates
- updates.forEach(update => {
- frm.set_value(update.fieldname, update.newValue);
- });
-
- // Обновляем дополнительные активности
- ETaxesCompany._updateAdditionalActivities(profile, frm);
-
- // Обновляем дочерние организации
- ETaxesCompany._updateAffiliateOrganizations(profile, frm);
-
- // НОВОЕ: Обновляем поля из справочников
- ETaxesCompany._updateDictionaryFields(profile, frm);
-
- frappe.show_alert({
- message: __('Company data updated from E-Taxes profile ({0} fields)', [updates.length]),
- indicator: 'green'
- }, 5);
-
- // Сохраняем изменения
- frm.save();
- },
- function() {
- frappe.show_alert({
- message: __('Update cancelled'),
- indicator: 'blue'
- }, 3);
- }
- );
- } else {
- // Проверяем есть ли дополнительные данные для обновления
- const hasAdditionalActivities = profile.additionalActivityCodes && profile.additionalActivityCodes.length > 0;
- const hasAffiliates = profile.affiliatesNames && profile.affiliatesNames.length > 0;
- const hasDictionaryFields = profile.taxAuthorityCode || profile.propertyType;
-
- if (hasAdditionalActivities || hasAffiliates || hasDictionaryFields) {
- let updateInfo = __('The following data will be updated:');
- if (hasAdditionalActivities) {
- updateInfo += `
${__('Additional activities')}: ${profile.additionalActivityCodes.length} ${__('activities')}`;
- }
- if (hasAffiliates) {
- updateInfo += `
${__('Affiliate organizations')}: ${profile.affiliatesNames.length} ${__('organizations')}`;
- }
- if (hasDictionaryFields) {
- updateInfo += `
${__('Dictionary fields')}: Tax Authority, Property Type`;
- }
-
- frappe.confirm(
- updateInfo + '
' + __('Continue?'),
- function() {
- if (hasAdditionalActivities) {
- ETaxesCompany._updateAdditionalActivities(profile, frm);
- }
- if (hasAffiliates) {
- ETaxesCompany._updateAffiliateOrganizations(profile, frm);
- }
- if (hasDictionaryFields) {
- ETaxesCompany._updateDictionaryFields(profile, frm);
- }
-
- // Автосохранение после добавления данных
- setTimeout(function() {
- frm.save();
- }, 500);
- },
- function() {
- frappe.show_alert({
- message: __('Update cancelled'),
- indicator: 'blue'
- }, 3);
- }
- );
- } else {
- frappe.show_alert({
- message: __('No new data to update'),
- indicator: 'blue'
- }, 3);
- }
- }
-
- } catch (e) {
- console.error('Error parsing profile data:', e);
- frappe.show_alert({
- message: __('Error parsing profile data: {0}', [e.message]),
- indicator: 'red'
- }, 5);
- }
- },
+ updateCompanyFromProfile: function(profileData, frm) {
+ try {
+ const profile = profileData.profile || profileData;
+ const updates = ETaxesCompany._parseProfileData(profile, frm);
+
+ // Проверяем есть ли дополнительные данные для обновления
+ const hasAdditionalActivities = profile.additionalActivityCodes && profile.additionalActivityCodes.length > 0;
+ const hasAffiliates = profile.affiliatesNames && profile.affiliatesNames.length > 0;
+ const hasDictionaryFields = profile.taxAuthorityCode || profile.propertyType;
+
+ // Если есть обновления полей или дополнительные данные
+ if (updates.length > 0 || hasAdditionalActivities || hasAffiliates || hasDictionaryFields) {
+ // Формируем сообщение для подтверждения
+ let confirmMessage = '';
+
+ if (updates.length > 0) {
+ confirmMessage = __('The following company fields will be updated with data from E-Taxes:') + '
';
+ let changesList = '';
+ updates.forEach(update => {
+ changesList += `- ${update.label}: ${update.oldValue || __('(empty)')} → ${update.newValue}
`;
+ });
+ changesList += '
';
+ confirmMessage += changesList;
+ }
+
+ // Добавляем информацию о дополнительных данных
+ let additionalInfo = '';
+ if (hasAdditionalActivities) {
+ additionalInfo += `
${__('Additional activities to be added')}: ${profile.additionalActivityCodes.length} ${__('activities')}`;
+ }
+ if (hasAffiliates) {
+ additionalInfo += `
${__('Affiliate organizations to be added')}: ${profile.affiliatesNames.length} ${__('organizations')}`;
+ }
+
+ if (!confirmMessage && additionalInfo) {
+ confirmMessage = __('The following data will be updated:');
+ }
+
+ confirmMessage += additionalInfo;
+ confirmMessage += '
' + __('Continue?');
+
+ frappe.confirm(
+ confirmMessage,
+ function() {
+ // ВАЖНО: Отключаем автосохранение перед массовыми изменениями
+ frm.disable_save();
+
+ // Apply all field updates
+ updates.forEach(update => {
+ frm.set_value(update.fieldname, update.newValue);
+ });
+
+ // Обновляем поля из справочников
+ if (hasDictionaryFields) {
+ ETaxesCompany._updateDictionaryFields(profile, frm);
+ }
+
+ // Задержка перед обновлением табличных частей
+ setTimeout(function() {
+ // Обновляем дополнительные активности
+ if (hasAdditionalActivities) {
+ ETaxesCompany._updateAdditionalActivities(profile, frm);
+ }
+
+ // Обновляем дочерние организации
+ if (hasAffiliates) {
+ ETaxesCompany._updateAffiliateOrganizations(profile, frm);
+ }
+
+ // Включаем сохранение обратно и сохраняем
+ setTimeout(function() {
+ frm.enable_save();
+ frm.save('Update', function() {
+ frappe.show_alert({
+ message: __('Company data updated from E-Taxes profile'),
+ indicator: 'green'
+ }, 5);
+ });
+ }, 500);
+ }, 300);
+ },
+ function() {
+ frappe.show_alert({
+ message: __('Update cancelled'),
+ indicator: 'blue'
+ }, 3);
+ }
+ );
+ } else {
+ frappe.show_alert({
+ message: __('No new data to update'),
+ indicator: 'blue'
+ }, 3);
+ }
+
+ } catch (e) {
+ console.error('Error parsing profile data:', e);
+ frappe.show_alert({
+ message: __('Error parsing profile data: {0}', [e.message]),
+ indicator: 'red'
+ }, 5);
+ }
+ },
/**
* Extract best phone number from profile
*/
- _extractPhoneNumber: function(profile) {
- // Priority: mobile > landline > contacts mobile > contacts home
- if (profile.mobilePhoneNumber) {
- return profile.mobilePhoneNumber;
- }
-
- if (profile.landlinePhoneNumber) {
- return profile.landlinePhoneNumber;
- }
-
- // Check contacts array
- if (profile.contacts && Array.isArray(profile.contacts)) {
- // Look for mobile first
- const mobileContact = profile.contacts.find(c => c.type === 'mobile' && c.contact);
- if (mobileContact) {
- return mobileContact.prefix ? `${mobileContact.prefix}${mobileContact.contact}` : mobileContact.contact;
- }
-
- // Then home phone
- const phoneContact = profile.contacts.find(c => c.type === 'homePhone' && c.contact);
- if (phoneContact) {
- return phoneContact.prefix ? `${phoneContact.prefix}${phoneContact.contact}` : phoneContact.contact;
- }
- }
-
- return null;
- },
+ _extractPhoneNumber: function(profile, preferredType = null) {
+ // If preferredType is specified, try to get that type first
+ if (preferredType === 'mobile' && profile.mobilePhoneNumber) {
+ return profile.mobilePhoneNumber;
+ }
+ if (preferredType === 'landline' && profile.landlinePhoneNumber) {
+ return profile.landlinePhoneNumber;
+ }
+
+ // Priority: mobile > landline > contacts mobile > contacts home
+ if (profile.mobilePhoneNumber) {
+ return profile.mobilePhoneNumber;
+ }
+
+ if (profile.landlinePhoneNumber) {
+ return profile.landlinePhoneNumber;
+ }
+
+ // Check contacts array
+ if (profile.contacts && Array.isArray(profile.contacts)) {
+ // Look for mobile first
+ const mobileContact = profile.contacts.find(c => c.type === 'mobile' && c.contact);
+ if (mobileContact) {
+ return mobileContact.prefix ? `${mobileContact.prefix}${mobileContact.contact}` : mobileContact.contact;
+ }
+
+ // Then home phone
+ const phoneContact = profile.contacts.find(c => c.type === 'homePhone' && c.contact);
+ if (phoneContact) {
+ return phoneContact.prefix ? `${phoneContact.prefix}${phoneContact.contact}` : phoneContact.contact;
+ }
+ }
+
+ return null;
+ },
/**
* Format VAT Info object as readable text
@@ -1044,221 +1289,332 @@ window.ETaxesCompany = {
/**
* Format parsed profile data as HTML
*/
- _formatParsedProfileHTML: function(profile) {
- let html = '';
-
- try {
- // Basic Company Information
- html += '
' + __('Company Information') + '
';
- html += '
';
-
- if (profile.companyName) {
- html += `| ${__('Company Name')} | ${profile.companyName} |
`;
- }
- if (profile.tin) {
- html += `| ${__('TIN')} | ${profile.tin} |
`;
- }
- if (profile.pin) {
- html += `| ${__('PIN')} | ${profile.pin} |
`;
- }
- if (profile.stateRegistrationDocumentNumber) {
- html += `| ${__('Registration Number')} | ${profile.stateRegistrationDocumentNumber} |
`;
- }
- if (profile.taxpayerRegistrationDate) {
- const regDate = moment(profile.taxpayerRegistrationDate).format('DD.MM.YYYY');
- html += `| ${__('Registration Date')} | ${regDate} |
`;
- }
- if (profile.taxpayerStatus) {
- html += `| ${__('Status')} | ${profile.taxpayerStatus} |
`;
- }
-
- html += '
';
-
- // Contact Information
- const phoneNumber = ETaxesCompany._extractPhoneNumber(profile);
- const address = profile.legalAddress || profile.actualAddress;
-
- if (phoneNumber || profile.email || address) {
- html += '
' + __('Contact Information') + '
';
- html += '
';
-
- if (phoneNumber) {
- html += `| ${__('Phone')} | ${phoneNumber} |
`;
- }
- if (profile.email) {
- html += `| ${__('Email')} | ${profile.email} |
`;
- }
- if (address) {
- html += `| ${__('Address')} | ${address} |
`;
- }
-
- html += '
';
- }
-
- // Business Information
- if (profile.mainActivity || profile.additionalActivityCodes || profile.legalFormCode || profile.criteriaOfBusinessEntity) {
- html += '
' + __('Business Information') + '
';
- html += '
';
-
- if (profile.mainActivity) {
- html += `| ${__('Main Activity Code')} | ${profile.mainActivity} |
`;
- }
- if (profile.additionalActivityCodes && profile.additionalActivityCodes.length > 0) {
- html += `| ${__('Additional Activity Codes')} | ${profile.additionalActivityCodes.join(', ')} |
`;
- }
- if (profile.legalFormCode) {
- html += `| ${__('Legal Form Code')} | ${profile.legalFormCode} |
`;
- }
- if (profile.criteriaOfBusinessEntity) {
- html += `| ${__('Business Entity Criteria')} | ${profile.criteriaOfBusinessEntity} |
`;
- }
-
- html += '
';
- }
-
- // Tax Information
- if (profile.taxRate || profile.vatInfo || profile.specialTaxRegime !== undefined || profile.taxAuthorityCode || profile.propertyType) {
- html += '
' + __('Tax Information') + '
';
- html += '
';
-
- if (profile.taxRate) {
- html += `| ${__('Tax Rate')} | ${profile.taxRate} |
`;
- }
- if (profile.specialTaxRegime !== undefined) {
- html += `| ${__('Special Tax Regime')} | ${profile.specialTaxRegime ? __('Yes') : __('No')} |
`;
- }
- if (profile.taxAuthorityCode) {
- html += `| ${__('Tax Authority Code')} | ${profile.taxAuthorityCode} |
`;
- }
- if (profile.propertyType) {
- html += `| ${__('Property Type')} | ${profile.propertyType} |
`;
- }
- if (profile.vatInfo) {
- const formattedVat = ETaxesCompany._formatVatInfo(profile.vatInfo);
- html += `| ${__('VAT Info')} | ${formattedVat} |
`;
- }
-
- html += '
';
- }
-
- // Organizational Structure
- if (profile.affiliatesNames) {
- html += '
' + __('Organizational Structure') + '
';
- html += '
';
-
- if (profile.affiliatesNames && profile.affiliatesNames.length > 0) {
- const affiliatesText = profile.affiliatesNames.join(', ');
- html += `| ${__('Affiliate Organizations')} | ${affiliatesText} |
`;
- }
-
- html += '
';
- }
-
- // Director Information
- if (profile.companyDirectorName || profile.chief) {
- html += '
' + __('Management Information') + '
';
- html += '
';
-
- if (profile.companyDirectorName) {
- html += `| ${__('Director Name')} | ${profile.companyDirectorName} |
`;
- }
- if (profile.chief) {
- const chiefName = `${profile.chief.name || ''} ${profile.chief.surname || ''} ${profile.chief.middleName || ''}`.trim();
- if (chiefName) {
- html += `| ${__('Chief')} | ${chiefName} |
`;
- }
- if (profile.chief.pin) {
- html += `| ${__('Chief PIN')} | ${profile.chief.pin} |
`;
- }
- }
- if (profile.employeeData && profile.employeeData.employeeCount) {
- html += `| ${__('Employee Count')} | ${profile.employeeData.employeeCount} |
`;
- }
-
- html += '
';
- }
-
- } catch (e) {
- html += '
' + __('Unable to parse profile data: {0}', [e.message]) + '
';
- }
-
- html += '
';
-
- // CSS с нормальным переносом строк
- html = `
-
- ` + html;
-
- return html;
- },
+ _formatParsedProfileHTML: function(profile) {
+ let html = '';
+
+ try {
+ // Basic Company Information
+ html += '
' + __('Company Information') + '
';
+ html += '
';
+
+ if (profile.companyName) {
+ html += `| ${__('Company Name')} | ${profile.companyName} |
`;
+ }
+ if (profile.tin) {
+ html += `| ${__('TIN')} | ${profile.tin} |
`;
+ }
+ if (profile.pin) {
+ html += `| ${__('PIN')} | ${profile.pin} |
`;
+ }
+ if (profile.stateRegistrationDocumentNumber) {
+ html += `| ${__('Registration Number')} | ${profile.stateRegistrationDocumentNumber} |
`;
+ }
+ if (profile.taxpayerRegistrationDate) {
+ const regDate = moment(profile.taxpayerRegistrationDate).format('DD.MM.YYYY');
+ html += `| ${__('Registration Date')} | ${regDate} |
`;
+ }
+ if (profile.stateRegistrationDocumentIssuedDate) {
+ const issuedDate = moment(profile.stateRegistrationDocumentIssuedDate).format('DD.MM.YYYY');
+ html += `| ${__('State Registration Document Issued Date')} | ${issuedDate} |
`;
+ }
+ if (profile.taxpayerStatus) {
+ const statusText = profile.taxpayerStatus === 'A' ? __('Active') : profile.taxpayerStatus;
+ html += `| ${__('Status')} | ${statusText} |
`;
+ }
+ if (profile.legalFormCode) {
+ html += `| ${__('Legal Form Code')} | ${profile.legalFormCode} |
`;
+ }
+ if (profile.tinType) {
+ html += `| ${__('TIN Type')} | ${profile.tinType} |
`;
+ }
+
+ html += '
';
+
+ // Contact Information
+ html += '
' + __('Contact Information') + '
';
+ html += '
';
+
+ if (profile.mobilePhoneNumber) {
+ html += `| ${__('Mobile Phone')} | ${profile.mobilePhoneNumber} |
`;
+ }
+ if (profile.landlinePhoneNumber) {
+ html += `| ${__('Landline Phone')} | ${profile.landlinePhoneNumber} |
`;
+ }
+ if (profile.email) {
+ html += `| ${__('Email')} | ${profile.email} |
`;
+ }
+ if (profile.legalAddress) {
+ html += `| ${__('Legal Address')} | ${profile.legalAddress} |
`;
+ }
+ if (profile.actualAddress) {
+ html += `| ${__('Actual Address')} | ${profile.actualAddress} |
`;
+ }
+ if (profile.addressForMail) {
+ html += `| ${__('Address for Mail')} | ${profile.addressForMail} |
`;
+ }
+
+ // Legal Address Details
+ if (profile.legalAddressObject && typeof profile.legalAddressObject === 'object') {
+ const addr = profile.legalAddressObject;
+ if (addr.postcode || addr.region || addr.locality || addr.street || addr.houseNumber || addr.roomNumber) {
+ html += `| ${__('Legal Address Details')} |
`;
+ if (addr.postcode) html += `| ${__('Postcode')} | ${addr.postcode} |
`;
+ if (addr.region) html += `| ${__('Region')} | ${addr.region} |
`;
+ if (addr.locality) html += `| ${__('Locality')} | ${addr.locality} |
`;
+ if (addr.street) html += `| ${__('Street')} | ${addr.street} |
`;
+ if (addr.houseNumber) html += `| ${__('House Number')} | ${addr.houseNumber} |
`;
+ if (addr.roomNumber) html += `| ${__('Room Number')} | ${addr.roomNumber} |
`;
+ }
+ }
+
+ html += '
';
+
+ // Business Information
+ html += '
' + __('Business Information') + '
';
+ html += '
';
+
+ if (profile.mainActivity) {
+ html += `| ${__('Main Activity Code')} | ${profile.mainActivity} |
`;
+ }
+ if (profile.additionalActivityCodes && profile.additionalActivityCodes.length > 0) {
+ html += `| ${__('Additional Activity Codes')} | ${profile.additionalActivityCodes.join(', ')} |
`;
+ }
+ if (profile.criteriaOfBusinessEntity) {
+ html += `| ${__('Business Entity Criteria')} | ${profile.criteriaOfBusinessEntity} |
`;
+ }
+ if (profile.activityGroup) {
+ html += `| ${__('Activity Group')} | ${profile.activityGroup} |
`;
+ }
+ if (profile.sportBettingOperator !== undefined) {
+ html += `| ${__('Sport Betting Operator')} | ${profile.sportBettingOperator ? __('Yes') : __('No')} |
`;
+ }
+ if (profile.hasActiveProductionObject !== undefined) {
+ html += `| ${__('Has Active Production Object')} | ${profile.hasActiveProductionObject ? __('Yes') : __('No')} |
`;
+ }
+
+ html += '
';
+
+ // Tax Information
+ html += '
' + __('Tax Information') + '
';
+ html += '
';
+
+ if (profile.taxRate) {
+ html += `| ${__('Tax Rate')} | ${profile.taxRate} |
`;
+ }
+ if (profile.specialTaxRegime !== undefined) {
+ html += `| ${__('Special Tax Regime')} | ${profile.specialTaxRegime ? __('Yes') : __('No')} |
`;
+ }
+ if (profile.taxAuthorityCode) {
+ html += `| ${__('Tax Authority Code')} | ${profile.taxAuthorityCode} |
`;
+ }
+ if (profile.propertyType) {
+ html += `| ${__('Property Type')} | ${profile.propertyType} |
`;
+ }
+ if (profile.taxSystemType) {
+ html += `| ${__('Tax System Type')} | ${profile.taxSystemType} |
`;
+ }
+ if (profile.taxSystems && profile.taxSystems.length > 0) {
+ html += `| ${__('Tax Systems')} | ${profile.taxSystems.join(', ')} |
`;
+ }
+ if (profile.isRisky !== undefined) {
+ html += `| ${__('Is Risky Taxpayer')} | ${profile.isRisky ? __('Yes') : __('No')} |
`;
+ }
+ if (profile.isTaxPayerInCancellationProcess !== undefined) {
+ html += `| ${__('In Cancellation Process')} | ${profile.isTaxPayerInCancellationProcess ? __('Yes') : __('No')} |
`;
+ }
+ if (profile.vatInfo) {
+ const formattedVat = ETaxesCompany._formatVatInfo(profile.vatInfo);
+ html += `| ${__('VAT Info')} | ${formattedVat} |
`;
+ }
+
+ html += '
';
+
+ // Organizational Structure
+ html += '
' + __('Organizational Structure') + '
';
+ html += '
';
+
+ if (profile.headOrganizationName && profile.headOrganizationName.length > 0) {
+ html += `| ${__('Parent Organization')} | ${profile.headOrganizationName.join(', ')} |
`;
+ }
+ if (profile.headOrganizationTin) {
+ html += `| ${__('Parent Organization TIN')} | ${profile.headOrganizationTin} |
`;
+ }
+ if (profile.affiliatesNames && profile.affiliatesNames.length > 0) {
+ const affiliatesText = profile.affiliatesNames.join(', ');
+ html += `| ${__('Affiliate Organizations')} | ${affiliatesText} |
`;
+ }
+ if (profile.affiliatesTins && profile.affiliatesTins.length > 0) {
+ const affiliatesTinsText = profile.affiliatesTins.join(', ');
+ html += `| ${__('Affiliate Organizations TINs')} | ${affiliatesTinsText} |
`;
+ }
+
+ html += '
';
+
+ // Management Information
+ html += '
' + __('Management Information') + '
';
+ html += '
';
+
+ if (profile.companyDirectorName) {
+ html += `| ${__('Director Name')} | ${profile.companyDirectorName} |
`;
+ }
+ if (profile.chief) {
+ const chiefName = `${profile.chief.name || ''} ${profile.chief.surname || ''} ${profile.chief.middleName || ''}`.trim();
+ if (chiefName) {
+ html += `| ${__('Chief')} | ${chiefName} |
`;
+ }
+ if (profile.chief.pin) {
+ html += `| ${__('Chief PIN')} | ${profile.chief.pin} |
`;
+ }
+ }
+ if (profile.employer && profile.employer.name) {
+ html += `| ${__('Employer')} | ${profile.employer.name} |
`;
+ if (profile.employer.position) {
+ html += `| ${__('Employer Position')} | ${profile.employer.position} |
`;
+ }
+ }
+ if (profile.isChiefOfAnyLegalEntity !== undefined) {
+ html += `| ${__('Is Chief of Any Legal Entity')} | ${profile.isChiefOfAnyLegalEntity ? __('Yes') : __('No')} |
`;
+ }
+ if (profile.employeeData && profile.employeeData.employeeCount !== undefined) {
+ html += `| ${__('Employee Count')} | ${profile.employeeData.employeeCount} |
`;
+ }
+
+ html += '
';
+
+ // Important Dates
+ if (profile.suspensionStartDate || profile.suspensionEndDate || profile.liquidationDate) {
+ html += '
' + __('Important Dates') + '
';
+ html += '
';
+
+ if (profile.suspensionStartDate) {
+ const suspStartDate = moment(profile.suspensionStartDate).format('DD.MM.YYYY');
+ html += `| ${__('Suspension Start Date')} | ${suspStartDate} |
`;
+ }
+ if (profile.suspensionEndDate) {
+ const suspEndDate = moment(profile.suspensionEndDate).format('DD.MM.YYYY');
+ html += `| ${__('Suspension End Date')} | ${suspEndDate} |
`;
+ }
+ if (profile.liquidationDate) {
+ const liqDate = moment(profile.liquidationDate).format('DD.MM.YYYY');
+ html += `| ${__('Liquidation Date')} | ${liqDate} |
`;
+ }
+
+ html += '
';
+ }
+
+ // Certificates Information (if any)
+ if (profile.certificates && profile.certificates.length > 0) {
+ html += '
' + __('Certificates') + '
';
+ html += '
';
+
+ profile.certificates.forEach((cert, index) => {
+ html += `| ${__('Certificate')} ${index + 1} |
`;
+ if (cert.taxpayerType) {
+ html += `| ${__('Type')} | ${cert.taxpayerType} |
`;
+ }
+ if (cert.legalInfo) {
+ if (cert.legalInfo.tin) html += `| ${__('TIN')} | ${cert.legalInfo.tin} |
`;
+ if (cert.legalInfo.name) html += `| ${__('Name')} | ${cert.legalInfo.name} |
`;
+ }
+ if (cert.individualInfo) {
+ if (cert.individualInfo.fin) html += `| ${__('FIN')} | ${cert.individualInfo.fin} |
`;
+ if (cert.individualInfo.name) html += `| ${__('Name')} | ${cert.individualInfo.name} |
`;
+ }
+ if (cert.position) {
+ html += `| ${__('Position')} | ${cert.position} |
`;
+ }
+ if (cert.hasAccess !== undefined) {
+ html += `| ${__('Has Access')} | ${cert.hasAccess ? __('Yes') : __('No')} |
`;
+ }
+ if (cert.liquidated !== undefined) {
+ html += `| ${__('Liquidated')} | ${cert.liquidated ? __('Yes') : __('No')} |
`;
+ }
+ });
+
+ html += '
';
+ }
+
+ } catch (e) {
+ html += '
' + __('Unable to parse profile data: {0}', [e.message]) + '
';
+ }
+
+ html += '
';
+
+ // CSS остается тем же
+ html = `
+
+ ` + html;
+
+ return html;
+ },
/**
* Helper functions for authentication dialogs
diff --git a/taxes_az/taxes_az/doctype/business_classification/business_classification.json b/taxes_az/taxes_az/doctype/business_classification/business_classification.json
index 5ec2bad..5f0976e 100644
--- a/taxes_az/taxes_az/doctype/business_classification/business_classification.json
+++ b/taxes_az/taxes_az/doctype/business_classification/business_classification.json
@@ -1,21 +1,21 @@
{
"actions": [],
- "creation": "2025-06-05 14:25:30.955525",
+ "creation": "2025-06-16 21:29:11.777085",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
- "section_break_t7i9"
+ "section_break_twaq"
],
"fields": [
{
- "fieldname": "section_break_t7i9",
+ "fieldname": "section_break_twaq",
"fieldtype": "Section Break"
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2025-06-05 14:34:53.228349",
+ "modified": "2025-06-16 21:29:44.750246",
"modified_by": "Administrator",
"module": "Taxes Az",
"name": "Business Classification",
diff --git a/taxes_az/taxes_az/doctype/company_additional_activity/__init__.py b/taxes_az/taxes_az/doctype/company_additional_activity/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/taxes_az/taxes_az/doctype/company_additional_activity/company_additional_activity.json b/taxes_az/taxes_az/doctype/company_additional_activity/company_additional_activity.json
new file mode 100644
index 0000000..4eb3ea4
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_additional_activity/company_additional_activity.json
@@ -0,0 +1,33 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-06-16 17:05:37.052445",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "activity_type"
+ ],
+ "fields": [
+ {
+ "fieldname": "activity_type",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Activity Type",
+ "options": "Main type of activity",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-06-16 17:05:37.052445",
+ "modified_by": "Administrator",
+ "module": "Taxes Az",
+ "name": "Company Additional Activity",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/company_additional_activity/company_additional_activity.py b/taxes_az/taxes_az/doctype/company_additional_activity/company_additional_activity.py
new file mode 100644
index 0000000..e2028ae
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_additional_activity/company_additional_activity.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2025, Jey Soft and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CompanyAdditionalActivity(Document):
+ pass
diff --git a/taxes_az/taxes_az/doctype/company_affiliate_organization/__init__.py b/taxes_az/taxes_az/doctype/company_affiliate_organization/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/taxes_az/taxes_az/doctype/company_affiliate_organization/company_affiliate_organization.json b/taxes_az/taxes_az/doctype/company_affiliate_organization/company_affiliate_organization.json
new file mode 100644
index 0000000..b658926
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_affiliate_organization/company_affiliate_organization.json
@@ -0,0 +1,41 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-06-16 17:05:37.121462",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "organization_name",
+ "organization_tin"
+ ],
+ "fields": [
+ {
+ "columns": 8,
+ "fieldname": "organization_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Organization Name",
+ "reqd": 1
+ },
+ {
+ "columns": 4,
+ "fieldname": "organization_tin",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Organization TIN"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-06-18 12:00:00.000000",
+ "modified_by": "Administrator",
+ "module": "Taxes Az",
+ "name": "Company Affiliate Organization",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/company_affiliate_organization/company_affiliate_organization.py b/taxes_az/taxes_az/doctype/company_affiliate_organization/company_affiliate_organization.py
new file mode 100644
index 0000000..8880bbc
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_affiliate_organization/company_affiliate_organization.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2025, Jey Soft and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CompanyAffiliateOrganization(Document):
+ pass
diff --git a/taxes_az/taxes_az/doctype/company_business_object/__init__.py b/taxes_az/taxes_az/doctype/company_business_object/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/taxes_az/taxes_az/doctype/company_business_object/company_business_object.json b/taxes_az/taxes_az/doctype/company_business_object/company_business_object.json
new file mode 100644
index 0000000..b8f3930
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_business_object/company_business_object.json
@@ -0,0 +1,90 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-06-16 17:05:37.233923",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "object_name",
+ "object_code",
+ "object_address",
+ "main_area",
+ "additional_area",
+ "activity_classification",
+ "main_activity_code",
+ "total_employees",
+ "temporary_employees"
+ ],
+ "fields": [
+ {
+ "fieldname": "object_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Object Name",
+ "reqd": 1
+ },
+ {
+ "fieldname": "object_code",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Object Code",
+ "reqd": 1
+ },
+ {
+ "fieldname": "object_address",
+ "fieldtype": "Text",
+ "in_list_view": 1,
+ "label": "Object Address"
+ },
+ {
+ "fieldname": "main_area",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Main Area (sq.m)"
+ },
+ {
+ "fieldname": "additional_area",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Additional Area (sq.m)"
+ },
+ {
+ "fieldname": "activity_classification",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Activity Classification",
+ "options": "P\u0259rak\u0259nd\u0259 ticar\u0259t\nTopdan ticar\u0259t\n\u0130ctimai ia\u015f\u0259\nEmaledici s\u0259naye\nTikinti\nXidm\u0259t\nDig\u0259r"
+ },
+ {
+ "fieldname": "main_activity_code",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Main Activity Code"
+ },
+ {
+ "fieldname": "total_employees",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "Total Employees"
+ },
+ {
+ "fieldname": "temporary_employees",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "Temporary Employees"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-06-16 17:05:37.233923",
+ "modified_by": "Administrator",
+ "module": "Taxes Az",
+ "name": "Company Business Object",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/company_business_object/company_business_object.py b/taxes_az/taxes_az/doctype/company_business_object/company_business_object.py
new file mode 100644
index 0000000..bf052a6
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_business_object/company_business_object.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2025, Jey Soft and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CompanyBusinessObject(Document):
+ pass
diff --git a/taxes_az/taxes_az/doctype/company_certificate_data/__init__.py b/taxes_az/taxes_az/doctype/company_certificate_data/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/taxes_az/taxes_az/doctype/company_certificate_data/company_certificate_data.json b/taxes_az/taxes_az/doctype/company_certificate_data/company_certificate_data.json
new file mode 100644
index 0000000..aed9aaa
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_certificate_data/company_certificate_data.json
@@ -0,0 +1,72 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-06-16 17:05:37.403337",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "certificate_type",
+ "entity_name",
+ "entity_voen",
+ "certificate_series",
+ "certificate_number",
+ "service_value"
+ ],
+ "fields": [
+ {
+ "fieldname": "certificate_type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Certificate Type",
+ "options": "Issued\nReceived",
+ "reqd": 1
+ },
+ {
+ "fieldname": "entity_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Entity Name",
+ "reqd": 1
+ },
+ {
+ "fieldname": "entity_voen",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Entity V\u00d6EN",
+ "reqd": 1
+ },
+ {
+ "fieldname": "certificate_series",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Certificate Series",
+ "length": 3
+ },
+ {
+ "fieldname": "certificate_number",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Certificate Number"
+ },
+ {
+ "description": "Only for received certificates",
+ "fieldname": "service_value",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Service Value"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-06-16 17:05:37.403337",
+ "modified_by": "Administrator",
+ "module": "Taxes Az",
+ "name": "Company Certificate Data",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/company_certificate_data/company_certificate_data.py b/taxes_az/taxes_az/doctype/company_certificate_data/company_certificate_data.py
new file mode 100644
index 0000000..fced6c5
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_certificate_data/company_certificate_data.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2025, Jey Soft and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CompanyCertificateData(Document):
+ pass
diff --git a/taxes_az/taxes_az/doctype/company_non_resident_service/__init__.py b/taxes_az/taxes_az/doctype/company_non_resident_service/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/taxes_az/taxes_az/doctype/company_non_resident_service/company_non_resident_service.json b/taxes_az/taxes_az/doctype/company_non_resident_service/company_non_resident_service.json
new file mode 100644
index 0000000..73f1e08
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_non_resident_service/company_non_resident_service.json
@@ -0,0 +1,93 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-06-16 17:05:37.524930",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "non_resident_name",
+ "country_of_residence",
+ "service_description",
+ "service_period",
+ "foreign_voen",
+ "payment_document_number",
+ "payment_date",
+ "amount_without_vat",
+ "vat_amount"
+ ],
+ "fields": [
+ {
+ "fieldname": "non_resident_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Non-Resident Name",
+ "reqd": 1
+ },
+ {
+ "fieldname": "country_of_residence",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Country of Residence",
+ "options": "T\u00fcrkiy\u0259\nRusiya\nG\u00fcrc\u00fcstan\nAlmaniya\n\u0130ngilt\u0259r\u0259\nAB\u015e\nDig\u0259r",
+ "reqd": 1
+ },
+ {
+ "fieldname": "service_description",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Service Description",
+ "reqd": 1
+ },
+ {
+ "fieldname": "service_period",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Service Period"
+ },
+ {
+ "fieldname": "foreign_voen",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Foreign V\u00d6EN"
+ },
+ {
+ "fieldname": "payment_document_number",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Payment Document Number"
+ },
+ {
+ "fieldname": "payment_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Payment Date"
+ },
+ {
+ "fieldname": "amount_without_vat",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Amount Without VAT",
+ "reqd": 1
+ },
+ {
+ "fieldname": "vat_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "VAT Amount",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-06-16 17:05:37.524930",
+ "modified_by": "Administrator",
+ "module": "Taxes Az",
+ "name": "Company Non Resident Service",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/company_non_resident_service/company_non_resident_service.py b/taxes_az/taxes_az/doctype/company_non_resident_service/company_non_resident_service.py
new file mode 100644
index 0000000..4281a38
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/company_non_resident_service/company_non_resident_service.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2025, Jey Soft and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CompanyNonResidentService(Document):
+ pass
diff --git a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js
index ff5785d..142cd8f 100644
--- a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js
+++ b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.js
@@ -1,33 +1,424 @@
-// Код для экспорта XML
frappe.ui.form.on('Declaration of value added tax', {
refresh: function(frm) {
- frm.add_custom_button(__('Export XML'), function() {
- frappe.call({
- method: 'taxes_az.taxes_az.doctype.xml_mapping_tool.xml_mapping_tool.generate_xml',
- args: {
- mapping_tool: 'Declaration of value added tax', // Замените на имя вашего документа XML Mapping Tool
- doctype: 'Declaration of value added tax', // Замените на имя DocType
- docname: frm.docname
- },
- callback: function(r) {
- if (r.message) {
- // Создаем временный элемент для скачивания
- var element = document.createElement('a');
- element.setAttribute('href', 'data:text/xml;charset=utf-8,' + encodeURIComponent(r.message));
- element.setAttribute('download', frm.docname + '.xml');
- element.style.display = 'none';
- document.body.appendChild(element);
- element.click();
- document.body.removeChild(element);
-
- frappe.show_alert({
- message: __('XML file generated and downloaded'),
- indicator: 'green'
- });
- }
- }
- });
- }, __('Actions'));
+ // Добавляем кнопку автозаполнения
+ if (!frm.is_new() && frm.doc.docstatus === 0) {
+ frm.add_custom_button(__('Avtomatik Doldur'), function() {
+ fill_declaration_automatically(frm);
+ }, __('Əməliyyatlar'));
+
+ // Стилизуем кнопку
+ frm.page.set_primary_action(__('Avtomatik Doldur'), function() {
+ fill_declaration_automatically(frm);
+ });
+ }
+
+ // Добавляем кнопку проверки
+ if (!frm.is_new()) {
+ frm.add_custom_button(__('Yoxla'), function() {
+ validate_declaration(frm);
+ }, __('Əməliyyatlar'));
+ }
+
+ // Скрываем/показываем вкладки в зависимости от данных
+ toggle_tabs_visibility(frm);
+ },
+
+ onload: function(frm) {
+ // Устанавливаем значения по умолчанию
+ if (frm.is_new()) {
+ set_default_values(frm);
+ }
+
+ // Настраиваем фильтры для полей
+ setup_field_filters(frm);
+ },
+
+ rüb: function(frm) {
+ // При изменении квартала обновляем период
+ update_period_display(frm);
+ },
+
+ il: function(frm) {
+ // При изменении года обновляем период
+ update_period_display(frm);
+ },
+
+ bəyan_ediləcək_məlumatım_yoxdur: function(frm) {
+ // Если отмечено "нет данных для декларирования"
+ if (frm.doc.bəyan_ediləcək_məlumatım_yoxdur) {
+ frappe.confirm(
+ 'Bütün məlumatlar silinəcək. Davam etmək istəyirsiniz?',
+ function() {
+ clear_all_tables(frm);
+ },
+ function() {
+ frm.set_value('bəyan_ediləcək_məlumatım_yoxdur', 0);
+ }
+ );
}
}
-);
\ No newline at end of file
+});
+
+// Функция автоматического заполнения
+function fill_declaration_automatically(frm) {
+ // Проверяем обязательные поля
+ if (!frm.doc.rüb || !frm.doc.il) {
+ frappe.msgprint({
+ title: __('Xəta'),
+ indicator: 'red',
+ message: __('Zəhmət olmasa rüb və il seçin')
+ });
+ return;
+ }
+
+ // Получаем компанию
+ let company = frm.doc.company || frappe.defaults.get_user_default("Company");
+
+ if (!company) {
+ frappe.msgprint({
+ title: __('Xəta'),
+ indicator: 'red',
+ message: __('Şirkət seçilməyib')
+ });
+ return;
+ }
+
+ // Показываем индикатор загрузки
+ frappe.show_progress('Doldurulur', 30, 100, 'Məlumatlar yüklənir...');
+
+ // Вызываем серверную функцию
+ frappe.call({
+ method: 'taxes_az.declaration_of_value_added_tax.fill_declaration_of_value_added_tax',
+ args: {
+ declaration_name: frm.doc.name,
+ company: company,
+ quarter: frm.doc.rüb,
+ year: frm.doc.il
+ },
+ callback: function(r) {
+ frappe.hide_progress();
+
+ if (r.message && r.message.status === 'success') {
+ frm.reload_doc();
+
+ // Показываем результат
+ let msg = `
+
+
Bəyannamə uğurla dolduruldu!
+
Dövriyyə: ${format_currency(r.message.total_turnover)} AZN
+
Hesablanmış ƏDV: ${format_currency(r.message.total_vat)} AZN
+
Əvəzləşdirilən ƏDV: ${format_currency(r.message.offset_vat)} AZN
+
+
${r.message.total_vat_payable >= 0 ? 'Büdcəyə ödənilməli' : 'Büdcədən qaytarılmalı'}:
+ ${format_currency(Math.abs(r.message.total_vat_payable))} AZN
+
+ `;
+
+ frappe.msgprint({
+ title: __('Nəticə'),
+ indicator: 'green',
+ message: msg
+ });
+
+ // Обновляем видимость вкладок
+ toggle_tabs_visibility(frm);
+ } else {
+ frappe.msgprint({
+ title: __('Xəta'),
+ indicator: 'red',
+ message: r.message.message || 'Xəta baş verdi'
+ });
+ }
+ },
+ error: function(err) {
+ frappe.hide_progress();
+ frappe.msgprint({
+ title: __('Xəta'),
+ indicator: 'red',
+ message: __('Server xətası: ') + err.message
+ });
+ }
+ });
+}
+
+// Функция проверки декларации
+function validate_declaration(frm) {
+ let errors = [];
+ let warnings = [];
+
+ // Проверка основных данных
+ if (!frm.doc.vöen) {
+ errors.push('VÖEN doldurulmayıb');
+ }
+
+ // Проверка таблиц
+ if (!frm.doc.table_qzfk || frm.doc.table_qzfk.length === 0) {
+ errors.push('Hesabat dövriyyəsi cədvəli boşdur');
+ }
+
+ // Проверка расчетов
+ let total_vat = 0;
+ let offset_vat = 0;
+
+ // Считаем начисленный НДС
+ (frm.doc.table_qzfk || []).forEach(row => {
+ if (row.göstəricilər && row.göstəricilər.startsWith('301')) {
+ total_vat += (row.ədvməbləği || 0);
+ }
+ });
+
+ // Считаем зачетный НДС
+ (frm.doc.table_zvvh || []).forEach(row => {
+ if (row.göstəricilər && row.göstəricilər !== '317. Əməliyyatlar üzrə CƏMİ') {
+ offset_vat += (row.əlavədəyərvergisininməbləği || 0);
+ }
+ });
+
+ // Проверка итоговой строки
+ let has_payable = false;
+ let has_refund = false;
+
+ (frm.doc.table_kyya || []).forEach(row => {
+ if (row.göstəricilər === '326. Büdcəyə ödənilməlidir') {
+ has_payable = true;
+ if (row.ədvməbləği !== (total_vat - offset_vat)) {
+ errors.push('Büdcəyə ödənilməli məbləğ düzgün hesablanmayıb');
+ }
+ }
+ if (row.göstəricilər === '327. Büdcədən qaytarılır') {
+ has_refund = true;
+ }
+ });
+
+ if (has_payable && has_refund) {
+ errors.push('Həm ödəniş, həm də qaytarma eyni vaxtda ola bilməz');
+ }
+
+ // Проверка приложений
+ if (frm.doc.table_gpfh && frm.doc.table_gpfh.length > 0) {
+ frm.doc.table_gpfh.forEach((row, idx) => {
+ if (!row.obyektinadı || !row.obyektinünvanı) {
+ warnings.push(`Əlavə 9: Sətir ${idx + 1} - obyekt məlumatları natamamdır`);
+ }
+ });
+ }
+
+ // Показываем результаты
+ show_validation_results(errors, warnings);
+}
+
+// Установка значений по умолчанию
+function set_default_values(frm) {
+ // Текущий квартал
+ let current_month = frappe.datetime.now_date().getMonth() + 1;
+ let current_quarter = Math.ceil(current_month / 3);
+ let quarter_map = {1: '1. Rüb', 2: '2. Rüb', 3: '3. Rüb', 4: '4. Rüb'};
+
+ frm.set_value('rüb', quarter_map[current_quarter]);
+ frm.set_value('il', frappe.datetime.now_date().getFullYear());
+ frm.set_value('vergidövrü', 'Rüblük');
+ frm.set_value('bəyannaməninnövü', 'Cari');
+
+ // Данные компании
+ let company = frappe.defaults.get_user_default("Company");
+ if (company) {
+ frappe.db.get_doc('Company', company).then(doc => {
+ frm.set_value('vöen', doc.tax_id);
+ frm.set_value('ödəyicitipi', doc.taxpayer_type || 'Hüquqi');
+ frm.set_value('vergininödəyicisininqrupu', doc.taxpayer_group);
+ frm.set_value('vergiödəyisininqrupu', doc.tax_payment_group);
+ frm.set_value('vergiorqanı', doc.tax_authority);
+ frm.set_value('valyutanınnövü', 'Azərbaycan manatı');
+
+ // НДС регистрация
+ if (doc.vat_registration_date) {
+ frm.set_value('ədvqeydiyyatınınqüvəyyəminmətarixi',
+ frappe.datetime.str_to_user(doc.vat_registration_date).replace(/-/g, ''));
+ }
+ if (doc.vat_certificate_number) {
+ frm.set_value('ədvbildirişininnsi', doc.vat_certificate_number);
+ }
+ });
+ }
+}
+
+// Настройка фильтров
+function setup_field_filters(frm) {
+ // Фильтр для выбора месяца в зависимости от квартала
+ frm.set_query('ay', function() {
+ let quarter_months = {
+ '1. Rüb': ['Yanvar', 'Fevral', 'Mart'],
+ '2. Rüb': ['Aprel', 'May', 'İyun'],
+ '3. Rüb': ['İyul', 'Avqust', 'Sentyabr'],
+ '4. Rüb': ['Oktyabr', 'Noyabr', 'Dekabr']
+ };
+
+ return {
+ filters: [
+ ['name', 'in', quarter_months[frm.doc.rüb] || []]
+ ]
+ };
+ });
+}
+
+// Скрытие/показ вкладок
+function toggle_tabs_visibility(frm) {
+ // Скрываем все вкладки приложений по умолчанию
+ let all_appendices = [
+ 'əlavə_1_tab', 'əlavə_2_tab', 'əlavə_3_tab', 'əlavə_4_tab',
+ 'əlavə_5_tab', 'əlavə_6_tab', 'əlavə_7_tab', 'əlavə_8_tab',
+ 'əlavə_9_tab', 'əlavə_10_tab'
+ ];
+
+ all_appendices.forEach(tab => {
+ frm.toggle_display(tab, false);
+ });
+
+ // Показываем только заполненные
+ // Приложение 3 - всегда для услуг
+ if (frm.doc.table_kdll && frm.doc.table_kdll.length > 0) {
+ frm.toggle_display('əlavə_3_tab', true);
+ }
+
+ // Приложение 7 - услуги нерезидентов
+ if (frm.doc.table_vonn && frm.doc.table_vonn.length > 0) {
+ frm.toggle_display('əlavə_7_tab', true);
+ }
+
+ // Приложение 9 - объекты
+ if (frm.doc.table_gpfh && frm.doc.table_gpfh.length > 0) {
+ frm.toggle_display('əlavə_9_tab', true);
+ }
+}
+
+// Обновление отображения периода
+function update_period_display(frm) {
+ if (frm.doc.rüb && frm.doc.il) {
+ let period_text = `${frm.doc.il}-ci il, ${frm.doc.rüb}`;
+ frm.set_intro(period_text);
+ }
+}
+
+// Очистка всех таблиц
+function clear_all_tables(frm) {
+ let tables = [
+ 'table_qzfk', 'table_ufli', 'table_zvvh', 'table_wtjt',
+ 'table_kyya', 'table_ftqr', 'table_rsid', 'table_vliw',
+ 'table_fdbw', 'table_anob', 'table_kdll', 'table_pycw',
+ 'table_hnwx', 'table_bchc', 'table_vonn', 'table_gpfh',
+ 'table_bgay', 'table_hnbv', 'table_kmis', 'table_twws'
+ ];
+
+ tables.forEach(table => {
+ frm.clear_table(table);
+ });
+
+ frm.refresh_fields();
+ frappe.show_alert({
+ message: __('Bütün cədvəllər təmizləndi'),
+ indicator: 'blue'
+ });
+}
+
+// Показ результатов валидации
+function show_validation_results(errors, warnings) {
+ let html = '';
+
+ if (errors.length > 0) {
+ html += '
Xətalar:
';
+ errors.forEach(err => {
+ html += `- ${err}
`;
+ });
+ html += '
';
+ }
+
+ if (warnings.length > 0) {
+ html += '
Xəbərdarlıqlar:
';
+ warnings.forEach(warn => {
+ html += `- ${warn}
`;
+ });
+ html += '
';
+ }
+
+ if (errors.length === 0 && warnings.length === 0) {
+ html += '
Heç bir xəta tapılmadı!
';
+ }
+
+ html += '
';
+
+ frappe.msgprint({
+ title: __('Yoxlama nəticəsi'),
+ indicator: errors.length > 0 ? 'red' : (warnings.length > 0 ? 'orange' : 'green'),
+ message: html
+ });
+}
+
+// Форматирование валюты
+function format_currency(amount) {
+ return frappe.format(amount, {fieldtype: 'Currency', currency: 'AZN'});
+}
+
+// Дополнительные обработчики для таблиц
+frappe.ui.form.on('Declaration of value added tax Hesabat Dovriyye uzre', {
+ ədvməbləği: function(frm, cdt, cdn) {
+ calculate_totals(frm);
+ }
+});
+
+frappe.ui.form.on('Declaration of value added tax Evezlesdirilen', {
+ əlavədəyərvergisininməbləği: function(frm, cdt, cdn) {
+ calculate_totals(frm);
+ }
+});
+
+// Пересчет итогов
+function calculate_totals(frm) {
+ let total_vat = 0;
+ let offset_vat = 0;
+
+ // Считаем начисленный НДС
+ (frm.doc.table_qzfk || []).forEach(row => {
+ if (row.göstəricilər && row.göstəricilər.startsWith('301')) {
+ total_vat += (row.ədvməbləği || 0);
+ }
+ });
+
+ // Считаем зачетный НДС
+ (frm.doc.table_zvvh || []).forEach(row => {
+ if (row.göstəricilər && row.göstəricilər !== '317. Əməliyyatlar üzrə CƏMİ') {
+ offset_vat += (row.əlavədəyərvergisininməbləği || 0);
+ }
+ });
+
+ // Обновляем итоговую таблицу
+ let net_vat = total_vat - offset_vat;
+
+ // Очищаем таблицу расчетов
+ frm.clear_table('table_kyya');
+
+ if (net_vat > 0) {
+ frm.add_child('table_kyya', {
+ göstəricilər: '326. Büdcəyə ödənilməlidir',
+ ədvməbləği: net_vat
+ });
+ } else if (net_vat < 0) {
+ frm.add_child('table_kyya', {
+ göstəricilər: '327. Büdcədən qaytarılır',
+ ədvməbləği: Math.abs(net_vat)
+ });
+ }
+
+ frm.refresh_field('table_kyya');
+}
+
+// Горячие клавиши
+frappe.ui.keys.add_shortcut({
+ shortcut: 'ctrl+shift+f',
+ action: () => {
+ let frm = cur_frm;
+ if (frm && frm.doc.doctype === 'Declaration of value added tax') {
+ fill_declaration_automatically(frm);
+ }
+ },
+ description: __('Avtomatik doldur')
+});
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py
index fadeb10..c92ef25 100644
--- a/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py
+++ b/taxes_az/taxes_az/doctype/declaration_of_value_added_tax/declaration_of_value_added_tax.py
@@ -1,9 +1,381 @@
-# Copyright (c) 2025, Jey Soft and contributors
+# Copyright (c) 2025, Your Company and contributors
# For license information, please see license.txt
-# import frappe
+import frappe
from frappe.model.document import Document
-
+from frappe import _
+from frappe.utils import flt, getdate, add_months, cint
+from datetime import datetime, date
+import calendar
class Declarationofvalueaddedtax(Document):
pass
+
+import frappe
+from frappe import _
+from datetime import datetime, date
+from dateutil.relativedelta import relativedelta
+import json
+
+@frappe.whitelist()
+def fill_vat_declaration(declaration_name, company, quarter, year):
+ """
+ Автоматическое заполнение НДС декларации для компании разработки ПО
+ """
+ # Получаем документ декларации
+ doc = frappe.get_doc("Declaration of value added tax", declaration_name)
+
+ # Определяем период (начало и конец квартала)
+ quarter_start, quarter_end = get_quarter_dates(quarter, year)
+
+ # Основные данные компании
+ company_doc = frappe.get_doc("Company", company)
+
+ # 1. ЗАПОЛНЯЕМ ОСНОВНУЮ ЧАСТЬ ДЕКЛАРАЦИИ
+
+ # Получаем доходы от разработки ПО (Sales Invoice)
+ software_income = get_software_development_income(company, quarter_start, quarter_end)
+
+ # Получаем входящий НДС (Purchase Invoice)
+ input_vat = get_input_vat(company, quarter_start, quarter_end)
+
+ # Заполняем основную таблицу "Hesabat dövründəki dövriyyə üzrə"
+ fill_main_turnover_table(doc, software_income, input_vat)
+
+ # 2. ЗАПОЛНЯЕМ ПРИЛОЖЕНИЕ 3 (Əlavə 3)
+ fill_appendix_3(doc, software_income, quarter_start, quarter_end)
+
+ # 3. ЗАПОЛНЯЕМ ПРИЛОЖЕНИЕ 7 (если есть услуги от нерезидентов)
+ non_resident_services = get_non_resident_services(company, quarter_start, quarter_end)
+ if non_resident_services:
+ fill_appendix_7(doc, non_resident_services)
+
+ # 4. ЗАПОЛНЯЕМ ПРИЛОЖЕНИЕ 9 (офисы)
+ fill_appendix_9(doc, company_doc)
+
+ # Сохраняем документ
+ doc.save()
+ frappe.db.commit()
+
+ return {
+ "status": "success",
+ "message": f"НДС декларация заполнена за {quarter} квартал {year} года",
+ "total_vat_payable": calculate_vat_payable(doc)
+ }
+
+def get_quarter_dates(quarter, year):
+ """Получаем даты начала и конца квартала"""
+ quarter_map = {
+ "1. Rüb": (1, 3),
+ "2. Rüb": (4, 6),
+ "3. Rüb": (7, 9),
+ "4. Rüb": (10, 12)
+ }
+
+ start_month, end_month = quarter_map[quarter]
+ quarter_start = date(year, start_month, 1)
+ quarter_end = date(year, end_month, 1) + relativedelta(months=1) - relativedelta(days=1)
+
+ return quarter_start, quarter_end
+
+def get_software_development_income(company, start_date, end_date):
+ """Получаем доходы от разработки ПО"""
+ # Выбираем все Sales Invoice за период
+ invoices = frappe.db.sql("""
+ SELECT
+ si.name,
+ si.customer,
+ si.total,
+ si.total_taxes_and_charges,
+ si.grand_total,
+ si.posting_date,
+ si.is_pos,
+ si.mode_of_payment
+ FROM `tabSales Invoice` si
+ WHERE si.company = %s
+ AND si.posting_date BETWEEN %s AND %s
+ AND si.docstatus = 1
+ AND si.is_return = 0
+ """, (company, start_date, end_date), as_dict=True)
+
+ # Группируем по типам операций
+ result = {
+ "total_without_vat": 0,
+ "total_vat": 0,
+ "cash_sales": 0,
+ "cashless_sales": 0,
+ "pos_sales": 0,
+ "invoices": invoices
+ }
+
+ for inv in invoices:
+ # Сумма без НДС
+ base_amount = inv.total or 0
+ vat_amount = inv.total_taxes_and_charges or 0
+
+ result["total_without_vat"] += base_amount
+ result["total_vat"] += vat_amount
+
+ # Определяем тип оплаты
+ if inv.is_pos:
+ result["pos_sales"] += base_amount
+ elif inv.mode_of_payment and "cash" in inv.mode_of_payment.lower():
+ result["cash_sales"] += base_amount
+ else:
+ result["cashless_sales"] += base_amount
+
+ return result
+
+def get_input_vat(company, start_date, end_date):
+ """Получаем входящий НДС"""
+ purchases = frappe.db.sql("""
+ SELECT
+ pi.name,
+ pi.supplier,
+ pi.total,
+ pi.total_taxes_and_charges,
+ pi.grand_total,
+ pi.posting_date,
+ s.supplier_type,
+ s.country
+ FROM `tabPurchase Invoice` pi
+ LEFT JOIN `tabSupplier` s ON pi.supplier = s.name
+ WHERE pi.company = %s
+ AND pi.posting_date BETWEEN %s AND %s
+ AND pi.docstatus = 1
+ AND pi.is_return = 0
+ """, (company, start_date, end_date), as_dict=True)
+
+ result = {
+ "total_without_vat": 0,
+ "total_vat": 0,
+ "resident_purchases": 0,
+ "resident_vat": 0,
+ "non_resident_purchases": 0,
+ "non_resident_vat": 0,
+ "purchases": purchases
+ }
+
+ for pur in purchases:
+ base_amount = pur.total or 0
+ vat_amount = pur.total_taxes_and_charges or 0
+
+ result["total_without_vat"] += base_amount
+ result["total_vat"] += vat_amount
+
+ # Определяем резидент или нет
+ if pur.country and pur.country != "Azerbaijan":
+ result["non_resident_purchases"] += base_amount
+ result["non_resident_vat"] += vat_amount
+ else:
+ result["resident_purchases"] += base_amount
+ result["resident_vat"] += vat_amount
+
+ return result
+
+def fill_main_turnover_table(doc, income_data, input_vat_data):
+ """Заполняем основную таблицу по обороту"""
+ # Очищаем существующие строки
+ doc.table_qzfk = []
+
+ # 301 - ƏDV-nə 18 faiz dərəcə ilə cəlb olunan əməliyyatlar
+ doc.append("table_qzfk", {
+ "göstəricilər": "301. ƏDV-nə 18 faiz dərəcə ilə cəlb olunan əməliyyatlar",
+ "təqdimedilmişmalişvəxidmətlərindəyəriədvnəzərəalınmadan": 0,
+ "daxilolmuşməbləğədvnəzərəalınmadan": 0,
+ "ədvməbləği": 0
+ })
+
+ # 301.1 - Malların təqdim edilməsi, işlərin görülməsi, xidmətlərin göstərilməsi
+ doc.append("table_qzfk", {
+ "göstəricilər": "301.1 Malların təqdim edilməsi, işlərin görülməsi, xidmətlərin göstərilməsi üzrə əməliyyatlar",
+ "təqdimedilmişmalişvəxidmətlərindəyəriədvnəzərəalınmadan": income_data["total_without_vat"],
+ "daxilolmuşməbləğədvnəzərəalınmadan": income_data["total_without_vat"], # Для услуг обычно равны
+ "ədvməbləği": income_data["total_vat"]
+ })
+
+ # 305 - Əməliyyatlar üzrə CƏMİ
+ total_turnover = income_data["total_without_vat"]
+ total_received = income_data["total_without_vat"]
+ total_vat = income_data["total_vat"]
+
+ doc.append("table_qzfk", {
+ "göstəricilər": "305. Əməliyyatlar üzrə CƏMİ",
+ "təqdimedilmişmalişvəxidmətlərindəyəriədvnəzərəalınmadan": total_turnover,
+ "daxilolmuşməbləğədvnəzərəalınmadan": total_received,
+ "ədvməbləği": total_vat
+ })
+
+ # Заполняем таблицу Əvəzləşdirilən (зачеты)
+ doc.table_zvvh = []
+
+ # 308 - Электронные счета-фактуры
+ if input_vat_data["resident_vat"] > 0:
+ doc.append("table_zvvh", {
+ "göstəricilər": "308. VM 175.1-ci maddəsinə əsasən alınmış elektron qaimə-fakturalar (elektron vergi hesab-fakturalar) üzrə nağdsız qaydada ödənilmiş məbləğ",
+ "ödənilmişməbləğədvnəzərəalınmadan": input_vat_data["resident_purchases"],
+ "əlavədəyərvergisininməbləği": input_vat_data["resident_vat"]
+ })
+
+ # 312 - Платежи нерезидентам (если есть)
+ if input_vat_data["non_resident_vat"] > 0:
+ doc.append("table_zvvh", {
+ "göstəricilər": "312. Vergi Məcəlləsinin 169.4-cü maddəsinə əsasən qeyri-rezidentə ödənilmiş məbləğ",
+ "ödənilmişməbləğədvnəzərəalınmadan": input_vat_data["non_resident_purchases"],
+ "əlavədəyərvergisininməbləği": input_vat_data["non_resident_vat"]
+ })
+
+ # 317 - Итого по зачетам
+ doc.append("table_zvvh", {
+ "göstəricilər": "317. Əməliyyatlar üzrə CƏMİ",
+ "ödənilmişməbləğədvnəzərəalınmadan": input_vat_data["total_without_vat"],
+ "əlavədəyərvergisininməbləği": input_vat_data["total_vat"]
+ })
+
+ # Заполняем Hesablaşmalar (расчеты с бюджетом)
+ doc.table_kyya = []
+
+ vat_payable = total_vat - input_vat_data["total_vat"]
+
+ if vat_payable > 0:
+ # 326 - К уплате в бюджет
+ doc.append("table_kyya", {
+ "göstəricilər": "326. Büdcəyə ödənilməlidir",
+ "ədvməbləği": vat_payable
+ })
+ else:
+ # 327 - К возврату из бюджета
+ doc.append("table_kyya", {
+ "göstəricilər": "327. Büdcədən qaytarılır",
+ "ədvməbləği": abs(vat_payable)
+ })
+
+def fill_appendix_3(doc, income_data, start_date, end_date):
+ """Заполняем Приложение 3 - детализация операций"""
+ # Таблица 1 - ƏDV tutulan dövriyyə
+ doc.table_kdll = []
+
+ # 301.1.2.6 - Прочие услуги (разработка ПО)
+ doc.append("table_kdll", {
+ "göstəricilər": "301.1.2.6 Sair işlərin görülməsi və xidmətlərin göstərilməsi üzrə əməliyyatlar",
+ "təqdimedilmişmalişvəxidmətlərindəyəriədvnəzərəalınmadan": income_data["total_without_vat"],
+ "daxilolmuşməbləğədvnəzərəalınmadan": income_data["total_without_vat"]
+ })
+
+def fill_appendix_7(doc, non_resident_services):
+ """Заполняем Приложение 7 - услуги нерезидентов"""
+ doc.table_vonn = []
+
+ for service in non_resident_services:
+ doc.append("table_vonn", {
+ "qeyrirezidentinadı": service["supplier_name"],
+ "rezidentolduğuölkəninadı": service["country"],
+ "görülmüşişinxidmətinadı": service["service_description"],
+ "görülmüşişindövrü": service["service_period"],
+ "rezidentolduğuölkədəkivöeni": service["foreign_tax_id"],
+ "büdcəyəödənişitəsdiqtəsdiqedənsənədinnsi": service["payment_doc_no"],
+ "sənədintarixi": service["payment_date"],
+ "ödənilmişümumiədvsizməbləği": service["amount_without_vat"],
+ "ödənilmişümumiədvməbləği": service["vat_amount"]
+ })
+
+def fill_appendix_9(doc, company_doc):
+ """Заполняем Приложение 9 - данные об офисах"""
+ doc.table_gpfh = []
+
+ # Получаем офисы из business_objects компании
+ if company_doc.business_objects:
+ for idx, obj in enumerate(company_doc.business_objects):
+ doc.append("table_gpfh", {
+ "sətrinkodu": idx + 1,
+ "obyektinadı": obj.object_name,
+ "obyektinkodu": obj.object_code,
+ "obyektinünvanı": obj.address,
+ "obyektinəsassahəsikvm": obj.main_area,
+ "obyektinəlavəsahəsikvm": obj.additional_area,
+ "obyektinfəaliyyətsahəsiüzrətəsnifatı": "İXTİSASLI, ELMİ VƏ TEXNİKİ FƏALİYYƏT",
+ "obyektinəsasfəaliyyətnövününkoduvəadı": "J62010 - Kompüter proqramlaşdırma fəaliyyəti",
+ "obyektüzrəişçilərincəmisayı": obj.total_employees,
+ "ocümlədənobyektüzrəmüvəqqətiişçilərinsayı": obj.temporary_employees,
+ "obyektüzrətəqdimedilmişmallarınişlərinxidmətlərindəyərimanatla": obj.turnover,
+ "obyekttəqdimedilmişmallaraəldəedilmişhasilatməbləği": obj.revenue
+ })
+
+def get_non_resident_services(company, start_date, end_date):
+ """Получаем услуги от нерезидентов"""
+ # Здесь нужно определить логику получения услуг от нерезидентов
+ # Например, из Purchase Invoice где поставщик - нерезидент
+
+ services = frappe.db.sql("""
+ SELECT
+ pi.name,
+ pi.supplier,
+ s.supplier_name,
+ s.country,
+ pi.total as amount_without_vat,
+ pi.total_taxes_and_charges as vat_amount,
+ pi.posting_date,
+ pi.bill_no
+ FROM `tabPurchase Invoice` pi
+ JOIN `tabSupplier` s ON pi.supplier = s.name
+ WHERE pi.company = %s
+ AND pi.posting_date BETWEEN %s AND %s
+ AND pi.docstatus = 1
+ AND s.country != 'Azerbaijan'
+ AND s.country IS NOT NULL
+ """, (company, start_date, end_date), as_dict=True)
+
+ result = []
+ for service in services:
+ result.append({
+ "supplier_name": service.supplier_name,
+ "country": service.country,
+ "service_description": "IT услуги", # Можно брать из описания в PI
+ "service_period": service.posting_date.strftime("%Y%m"),
+ "foreign_tax_id": "", # Нужно добавить поле в Supplier
+ "payment_doc_no": service.bill_no or service.name,
+ "payment_date": service.posting_date.strftime("%Y%m%d"),
+ "amount_without_vat": service.amount_without_vat,
+ "vat_amount": service.vat_amount or (service.amount_without_vat * 0.18)
+ })
+
+ return result
+
+def calculate_vat_payable(doc):
+ """Рассчитываем НДС к уплате"""
+ # Берем из таблицы Hesablaşmalar
+ for row in doc.table_kyya:
+ if row.göstəricilər == "326. Büdcəyə ödənilməlidir":
+ return row.ədvməbləği
+ elif row.göstəricilər == "327. Büdcədən qaytarılır":
+ return -row.ədvməbləği
+ return 0
+
+# Функция для вызова из UI
+@frappe.whitelist()
+def create_and_fill_vat_declaration(company, quarter, year):
+ """Создать и заполнить НДС декларацию"""
+ # Создаем новый документ
+ doc = frappe.new_doc("Declaration of value added tax")
+
+ # Базовые поля
+ doc.vergidövrü = "Rüblük"
+ doc.rüb = quarter
+ doc.il = int(year)
+
+ # Данные компании
+ company_doc = frappe.get_doc("Company", company)
+ doc.vöen = company_doc.tax_id
+ doc.adı = company_doc.company_name
+ doc.vergiorqanı = company_doc.tax_authority or "Dövlət Vergi xidmətinin Bakı şəhəri Kiçik Sahibkarlıqla İş üzrə Baş idarəsi"
+
+ # Сохраняем документ
+ doc.insert()
+
+ # Заполняем данными
+ result = fill_vat_declaration(doc.name, company, quarter, year)
+
+ return {
+ "declaration_name": doc.name,
+ "result": result
+ }
\ No newline at end of file