Fixed bugs, added new tabs and subtabs, load from everywhere and etc

This commit is contained in:
Ali 2025-07-23 12:39:40 +04:00
parent 85f37a3f67
commit f0c19854fe
21 changed files with 2882 additions and 2157 deletions

File diff suppressed because it is too large Load Diff

View File

@ -851,12 +851,19 @@ function process_invoices_for_items(invoices, token, accumulated_data, current_i
const outboxText = accumulated_data.outbox_count ? const outboxText = accumulated_data.outbox_count ?
__(' and {0} sales invoices', [accumulated_data.outbox_count]) : ''; __(' and {0} sales invoices', [accumulated_data.outbox_count]) : '';
let message = __('Created {0} unique items. Skipped {1} duplicates. Total invoices processed: {2}{3}{4}',
[accumulated_data.created_count, accumulated_data.skipped_count,
accumulated_data.total_invoices, inboxText, outboxText]);
// Добавляем информацию об обновлениях, если есть
if (accumulated_data.updated_count > 0) {
message += __(' Updated {0} existing items with new source information.', [accumulated_data.updated_count]);
}
frappe.msgprint({ frappe.msgprint({
title: __('Items Loaded Successfully'), title: __('Items Loaded Successfully'),
indicator: 'green', indicator: 'green',
message: __('Created {0} unique items. Skipped {1} duplicates. Total invoices processed: {2}{3}{4}', message: message
[accumulated_data.created_count, accumulated_data.skipped_count,
accumulated_data.total_invoices, inboxText, outboxText])
}); });
// ИСПРАВЛЕНО: Refresh list с задержкой для уверенности в очистке состояния // ИСПРАВЛЕНО: Refresh list с задержкой для уверенности в очистке состояния
@ -875,13 +882,17 @@ function process_invoices_for_items(invoices, token, accumulated_data, current_i
// Улучшенная логика определения источника // Улучшенная логика определения источника
let sourceLabel; let sourceLabel;
let sourceType;
if (source === 'inbox') { if (source === 'inbox') {
sourceLabel = __('Purchase'); sourceLabel = __('Purchase');
sourceType = 'purchase';
} else if (source === 'outbox') { } else if (source === 'outbox') {
sourceLabel = __('Sales'); sourceLabel = __('Sales');
sourceType = 'sales';
} else { } else {
// Если _source не установлен или имеет неожиданное значение, считаем как Purchase // Если _source не установлен или имеет неожиданное значение, считаем как Purchase
sourceLabel = __('Purchase'); sourceLabel = __('Purchase');
sourceType = 'purchase';
// Добавляем отладочную информацию в консоль // Добавляем отладочную информацию в консоль
console.log('Warning: Invoice', serial_number, 'has unexpected _source:', source); console.log('Warning: Invoice', serial_number, 'has unexpected _source:', source);
} }
@ -899,12 +910,14 @@ function process_invoices_for_items(invoices, token, accumulated_data, current_i
method: 'invoice_az.api.process_single_invoice_for_items', method: 'invoice_az.api.process_single_invoice_for_items',
args: { args: {
'token': token, 'token': token,
'invoice_id': invoice_id 'invoice_id': invoice_id,
'source_type': sourceType // НОВОЕ: передаем тип источника
}, },
callback: function(r) { callback: function(r) {
if (r.message && r.message.success) { if (r.message && r.message.success) {
// Обновляем накопленные данные // Обновляем накопленные данные
accumulated_data.created_count += r.message.created_count || 0; accumulated_data.created_count += r.message.created_count || 0;
accumulated_data.updated_count += r.message.updated_count || 0; // НОВОЕ: учитываем обновления
accumulated_data.skipped_count += r.message.skipped_count || 0; accumulated_data.skipped_count += r.message.skipped_count || 0;
accumulated_data.failed_count += r.message.failed_count || 0; accumulated_data.failed_count += r.message.failed_count || 0;

View File

@ -9,7 +9,15 @@
"etaxes_item_code", "etaxes_item_code",
"etaxes_unit", "etaxes_unit",
"etaxes_price", "etaxes_price",
"product_group_section",
"etaxes_product_group_code",
"etaxes_product_group_name",
"etaxes_product_group_type",
"is_service_item",
"source_section",
"source_invoice", "source_invoice",
"is_from_purchase",
"is_from_sales",
"status", "status",
"mapped_item", "mapped_item",
"item_creation_section", "item_creation_section",
@ -50,11 +58,62 @@
"options": "AZN", "options": "AZN",
"read_only": 1 "read_only": 1
}, },
{
"fieldname": "product_group_section",
"fieldtype": "Section Break",
"label": "Product Group Information"
},
{
"fieldname": "etaxes_product_group_code",
"fieldtype": "Data",
"label": "Product Group Code",
"length": 50,
"read_only": 1
},
{
"fieldname": "etaxes_product_group_name",
"fieldtype": "Data",
"label": "Product Group Name",
"length": 1000,
"read_only": 1
},
{
"fieldname": "etaxes_product_group_type",
"fieldtype": "Data",
"label": "Product Group Type",
"read_only": 1
},
{
"default": "0",
"fieldname": "is_service_item",
"fieldtype": "Check",
"label": "Is Service Item",
"read_only": 1
},
{
"fieldname": "source_section",
"fieldtype": "Section Break",
"label": "Source Information"
},
{ {
"description": "Source invoice number", "description": "Source invoice number",
"fieldname": "source_invoice", "fieldname": "source_invoice",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Source", "label": "Source Invoice",
"read_only": 1
},
{
"default": "0",
"fieldname": "is_from_purchase",
"fieldtype": "Check",
"label": "From Purchase Invoice",
"read_only": 1
},
{
"default": "0",
"fieldname": "is_from_sales",
"fieldtype": "Check",
"label": "From Sales Invoice",
"read_only": 1 "read_only": 1
}, },
{ {
@ -101,7 +160,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2025-07-09 20:19:56.319865", "modified": "2025-07-16 20:19:56.319865",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Invoice Az", "module": "Invoice Az",
"name": "E-Taxes Item", "name": "E-Taxes Item",

View File

@ -14,6 +14,7 @@
"uom", "uom",
"is_stock_item", "is_stock_item",
"is_purchase_item", "is_purchase_item",
"is_sales_item",
"item_tax_template" "item_tax_template"
], ],
"fields": [ "fields": [
@ -83,6 +84,13 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Purchase Item" "label": "Is Purchase Item"
}, },
{
"default": "0",
"description": "Overrides default Is Sales Item setting",
"fieldname": "is_sales_item",
"fieldtype": "Check",
"label": "Is Sales Item"
},
{ {
"description": "Overrides default Item Tax Template setting", "description": "Overrides default Item Tax Template setting",
"fieldname": "item_tax_template", "fieldname": "item_tax_template",
@ -93,7 +101,7 @@
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-05-23 16:00:32.090354", "modified": "2025-07-16 16:00:32.090354",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Invoice Az", "module": "Invoice Az",
"name": "E-Taxes Item Mapping", "name": "E-Taxes Item Mapping",

View File

@ -8,7 +8,8 @@
"date", "date",
"total", "total",
"party", "party",
"etaxes_id" "etaxes_id",
"type"
], ],
"fields": [ "fields": [
{ {
@ -30,11 +31,17 @@
"fieldname": "etaxes_id", "fieldname": "etaxes_id",
"fieldtype": "Data", "fieldtype": "Data",
"label": "etaxes_id" "label": "etaxes_id"
},
{
"fieldname": "type",
"fieldtype": "Select",
"label": "type",
"options": "Sales\nPurchase"
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2025-05-31 14:13:09.870421", "modified": "2025-07-22 18:16:56.347313",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Invoice Az", "module": "Invoice Az",
"name": "E-Taxes Sales", "name": "E-Taxes Sales",

View File

@ -1,233 +1,272 @@
{ {
"actions": [], "actions": [],
"allow_rename": 1, "allow_rename": 1,
"autoname": "field:settings_name", "autoname": "field:settings_name",
"creation": "2025-05-06 12:00:00.000000", "creation": "2025-05-06 12:00:00",
"doctype": "DocType", "doctype": "DocType",
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"main_tab", "main_tab",
"settings_name", "settings_name",
"is_default", "is_default",
"is_active", "is_active",
"general_section", "general_section",
"similarity_threshold", "similarity_threshold",
"consider_azeri_chars", "consider_azeri_chars",
"default_item_settings_section", "default_item_settings_section",
"default_item_group", "default_item_group",
"default_uom", "default_uom",
"is_stock_item", "default_item_tax_template",
"is_purchase_item", "default_party_settings_section",
"default_item_tax_template", "default_supplier_group",
"default_party_settings_section", "default_customer_group",
"default_supplier_group", "default_payment_terms",
"default_customer_group", "items_tab",
"default_payment_terms", "items_list_html",
"data_upload_tab", "customers_tab",
"mappings_tab", "customers_list_html",
"item_mappings_section", "suppliers_tab",
"item_mappings", "suppliers_list_html",
"customer_mappings_section", "units_tab",
"customer_mappings", "units_list_html",
"supplier_mappings_section", "mappings_tab",
"supplier_mappings", "item_mappings_section",
"party_mappings_section", "item_mappings",
"party_mappings", "customer_mappings_section",
"unit_mappings_section", "customer_mappings",
"unit_mappings" "supplier_mappings_section",
], "supplier_mappings",
"fields": [ "unit_mappings_section",
{ "unit_mappings",
"fieldname": "main_tab", "data_tab",
"fieldtype": "Tab Break", "html_nbtb"
"label": "Settings" ],
}, "fields": [
{ {
"fieldname": "settings_name", "fieldname": "main_tab",
"fieldtype": "Data", "fieldtype": "Tab Break",
"label": "Settings Name", "label": "Settings"
"reqd": 1, },
"unique": 1 {
}, "fieldname": "settings_name",
{ "fieldtype": "Data",
"fieldname": "is_default", "in_list_view": 1,
"fieldtype": "Check", "label": "Settings Name",
"label": "Is Default", "reqd": 1,
"default": 0 "unique": 1
}, },
{ {
"fieldname": "is_active", "default": "0",
"fieldtype": "Check", "fieldname": "is_default",
"label": "Is Active", "fieldtype": "Check",
"default": 1 "label": "Is Default"
}, },
{ {
"fieldname": "general_section", "default": "1",
"fieldtype": "Section Break", "fieldname": "is_active",
"label": "General Settings" "fieldtype": "Check",
}, "label": "Is Active"
{ },
"fieldname": "similarity_threshold", {
"fieldtype": "Percent", "fieldname": "general_section",
"label": "Similarity Threshold (%)", "fieldtype": "Section Break",
"default": 95, "label": "General Settings"
"description": "Minimum similarity percentage for automatic mapping" },
}, {
{ "default": "95",
"fieldname": "consider_azeri_chars", "description": "Minimum similarity percentage for automatic mapping",
"fieldtype": "Check", "fieldname": "similarity_threshold",
"label": "Consider Azerbaijani Characters", "fieldtype": "Percent",
"default": 1, "label": "Similarity Threshold (%)"
"description": "Consider replacement of Azerbaijani letters with Latin equivalents when matching" },
}, {
{ "default": "1",
"fieldname": "default_item_settings_section", "description": "Consider replacement of Azerbaijani letters with Latin equivalents when matching",
"fieldtype": "Section Break", "fieldname": "consider_azeri_chars",
"label": "Default Item Settings" "fieldtype": "Check",
}, "label": "Consider Azerbaijani Characters"
{ },
"fieldname": "default_item_group", {
"fieldtype": "Link", "fieldname": "default_item_settings_section",
"label": "Default Item Group", "fieldtype": "Section Break",
"options": "Item Group", "label": "Default Item Settings"
"reqd": 1 },
}, {
{ "fieldname": "default_item_group",
"fieldname": "default_uom", "fieldtype": "Link",
"fieldtype": "Link", "in_list_view": 1,
"label": "Default Unit of Measure", "label": "Default Item Group",
"options": "UOM", "options": "Item Group",
"reqd": 1 "reqd": 1
}, },
{ {
"fieldname": "is_stock_item", "fieldname": "default_uom",
"fieldtype": "Check", "fieldtype": "Link",
"label": "Is Stock Item", "in_list_view": 1,
"default": 1 "label": "Default Unit of Measure",
}, "options": "UOM",
{ "reqd": 1
"fieldname": "is_purchase_item", },
"fieldtype": "Check", {
"label": "Is Purchase Item", "fieldname": "default_item_tax_template",
"default": 1 "fieldtype": "Link",
}, "label": "Default Item Tax Template",
{ "options": "Item Tax Template"
"fieldname": "default_item_tax_template", },
"fieldtype": "Link", {
"label": "Default Item Tax Template", "fieldname": "default_party_settings_section",
"options": "Item Tax Template" "fieldtype": "Section Break",
}, "label": "Default Business Partner Settings"
{ },
"fieldname": "default_party_settings_section", {
"fieldtype": "Section Break", "fieldname": "default_supplier_group",
"label": "Default Business Partner Settings" "fieldtype": "Link",
}, "label": "Default Supplier Group",
{ "options": "Supplier Group"
"fieldname": "default_supplier_group", },
"fieldtype": "Link", {
"label": "Default Supplier Group", "fieldname": "default_customer_group",
"options": "Supplier Group" "fieldtype": "Link",
}, "label": "Default Customer Group",
{ "options": "Customer Group"
"fieldname": "default_customer_group", },
"fieldtype": "Link", {
"label": "Default Customer Group", "fieldname": "default_payment_terms",
"options": "Customer Group" "fieldtype": "Link",
}, "label": "Default Payment Terms",
{ "options": "Payment Terms Template"
"fieldname": "default_payment_terms", },
"fieldtype": "Link", {
"label": "Default Payment Terms", "fieldname": "items_tab",
"options": "Payment Terms Template" "fieldtype": "Tab Break",
}, "js_parent_subtab": "Data tab",
{ "label": "Items"
"fieldname": "data_upload_tab", },
"fieldtype": "Tab Break", {
"label": "Data Upload from Website" "fieldname": "items_list_html",
}, "fieldtype": "HTML",
{ "label": "Items List",
"fieldname": "mappings_tab", "options": "<div id=\"items-list-container\">Loading items...</div>"
"fieldtype": "Tab Break", },
"label": "Mappings" {
}, "fieldname": "customers_tab",
{ "fieldtype": "Tab Break",
"fieldname": "item_mappings_section", "js_parent_subtab": "Data tab",
"fieldtype": "Section Break", "label": "Customers"
"label": "Item Mappings" },
}, {
{ "fieldname": "customers_list_html",
"fieldname": "item_mappings", "fieldtype": "HTML",
"fieldtype": "Table", "label": "Customers List",
"label": "Item Mappings", "options": "<div id=\"customers-list-container\">Loading customers...</div>"
"options": "E-Taxes Item Mapping" },
}, {
{ "fieldname": "suppliers_tab",
"fieldname": "customer_mappings_section", "fieldtype": "Tab Break",
"fieldtype": "Section Break", "js_parent_subtab": "Data tab",
"label": "Customer Mappings" "label": "Suppliers"
}, },
{ {
"fieldname": "customer_mappings", "fieldname": "suppliers_list_html",
"fieldtype": "Table", "fieldtype": "HTML",
"label": "Customer Mappings", "label": "Suppliers List",
"options": "E-Taxes Customer Mappings" "options": "<div id=\"suppliers-list-container\">Loading suppliers...</div>"
}, },
{ {
"fieldname": "supplier_mappings_section", "fieldname": "units_tab",
"fieldtype": "Section Break", "fieldtype": "Tab Break",
"label": "Supplier Mappings" "js_parent_subtab": "Data tab",
}, "label": "Units"
{ },
"fieldname": "supplier_mappings", {
"fieldtype": "Table", "fieldname": "units_list_html",
"label": "Supplier Mappings", "fieldtype": "HTML",
"options": "E-Taxes Supplier Mappings" "label": "Units List",
}, "options": "<div id=\"units-list-container\">Loading units...</div>"
{ },
"fieldname": "party_mappings_section", {
"fieldtype": "Section Break", "fieldname": "mappings_tab",
"label": "Business Partner Mappings (Legacy)" "fieldtype": "Tab Break",
}, "label": "Mappings"
{ },
"fieldname": "party_mappings", {
"fieldtype": "Table", "fieldname": "item_mappings_section",
"label": "Business Partner Mappings", "fieldtype": "Section Break",
"options": "E-Taxes Party Mapping" "label": "Item Mappings"
}, },
{ {
"fieldname": "unit_mappings_section", "fieldname": "item_mappings",
"fieldtype": "Section Break", "fieldtype": "Table",
"label": "Unit of Measure Mappings" "label": "Item Mappings",
}, "options": "E-Taxes Item Mapping"
{ },
"fieldname": "unit_mappings", {
"fieldtype": "Table", "fieldname": "customer_mappings_section",
"label": "Unit of Measure Mappings", "fieldtype": "Section Break",
"options": "E-Taxes Unit Mapping" "label": "Customer Mappings"
} },
], {
"index_web_pages_for_search": 1, "fieldname": "customer_mappings",
"links": [], "fieldtype": "Table",
"modified": "2025-07-11 14:00:00.000000", "label": "Customer Mappings",
"modified_by": "Administrator", "options": "E-Taxes Customer Mappings"
"module": "Invoice Az", },
"name": "E-Taxes Settings", {
"owner": "Administrator", "fieldname": "supplier_mappings_section",
"permissions": [ "fieldtype": "Section Break",
{ "label": "Supplier Mappings"
"create": 1, },
"delete": 1, {
"email": 1, "fieldname": "supplier_mappings",
"export": 1, "fieldtype": "Table",
"print": 1, "label": "Supplier Mappings",
"read": 1, "options": "E-Taxes Supplier Mappings"
"report": 1, },
"role": "System Manager", {
"share": 1, "fieldname": "unit_mappings_section",
"write": 1 "fieldtype": "Section Break",
} "label": "Unit of Measure Mappings"
], },
"sort_field": "modified", {
"sort_order": "DESC", "fieldname": "unit_mappings",
"track_changes": 1 "fieldtype": "Table",
"label": "Unit of Measure Mappings",
"options": "E-Taxes Unit Mapping"
},
{
"fieldname": "html_nbtb",
"fieldtype": "HTML"
},
{
"fieldname": "data_tab",
"fieldtype": "Tab Break",
"label": "Data tab"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-07-18 18:13:40.328160",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Settings",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
} }

View File

@ -244,7 +244,9 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
customer_mappings = {} customer_mappings = {}
for mapping in settings.customer_mappings: for mapping in settings.customer_mappings:
if mapping.etaxes_customer_name and mapping.erp_customer: if mapping.etaxes_customer_name and mapping.erp_customer:
key = f"{mapping.etaxes_customer_name.lower()}|{mapping.etaxes_tax_id.lower() if mapping.etaxes_tax_id else ''}" # ИСПРАВЛЕНИЕ: Обрезаем ключ до 140 символов
customer_name = mapping.etaxes_customer_name[:140] if len(mapping.etaxes_customer_name) > 140 else mapping.etaxes_customer_name
key = f"{customer_name.lower()}|{mapping.etaxes_tax_id.lower() if mapping.etaxes_tax_id else ''}"
customer_mappings[key] = mapping.erp_customer customer_mappings[key] = mapping.erp_customer
# Get default warehouse # Get default warehouse
@ -278,9 +280,12 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
else: else:
so = frappe.new_doc('Sales Order') so = frappe.new_doc('Sales Order')
# ИЗМЕНЕНО: Set customer используя customer_mappings # ИЗМЕНЕНО: Set customer используя customer_mappings с обрезкой
receiver = invoice_data.get('receiver', {}) receiver = invoice_data.get('receiver', {})
receiver_key = f"{receiver.get('name', '').lower()}|{receiver.get('tin', '').lower()}" receiver_name = receiver.get('name', '')
# ИСПРАВЛЕНИЕ: Обрезаем название клиента до 140 символов для поиска
receiver_name_truncated = receiver_name[:140] if len(receiver_name) > 140 else receiver_name
receiver_key = f"{receiver_name_truncated.lower()}|{receiver.get('tin', '').lower()}"
if receiver_key in customer_mappings and customer_mappings[receiver_key]: if receiver_key in customer_mappings and customer_mappings[receiver_key]:
so.customer = customer_mappings[receiver_key] so.customer = customer_mappings[receiver_key]
@ -335,12 +340,15 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
item_name = re.sub(r'\s+', ' ', item.get("productName", "")).strip() item_name = re.sub(r'\s+', ' ', item.get("productName", "")).strip()
item_code = item.get("itemId", "") or f"CODE-{invoice_data.get('serialNumber', '')}" item_code = item.get("itemId", "") or f"CODE-{invoice_data.get('serialNumber', '')}"
# Ищем соответствие товара # ИСПРАВЛЕНИЕ: Обрезаем название товара до 140 символов для поиска
mapped_item = item_mappings.get(item_name) item_name_truncated = item_name[:140] if len(item_name) > 140 else item_name
# Ищем соответствие товара сначала по обрезанному названию
mapped_item = item_mappings.get(item_name_truncated)
# Если не найдено, ищем без учета регистра # Если не найдено, ищем без учета регистра
if not mapped_item: if not mapped_item:
item_name_lower = item_name.lower() item_name_lower = item_name_truncated.lower()
for mapping_key, mapping_value in item_mappings.items(): for mapping_key, mapping_value in item_mappings.items():
if mapping_key.lower() == item_name_lower: if mapping_key.lower() == item_name_lower:
mapped_item = mapping_value mapped_item = mapping_value
@ -348,7 +356,7 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
# Если все еще не найдено, ищем с нормализацией азербайджанских символов # Если все еще не найдено, ищем с нормализацией азербайджанских символов
if not mapped_item: if not mapped_item:
normalized_input = normalize_string(item_name, consider_azeri=True) normalized_input = normalize_string(item_name_truncated, consider_azeri=True)
for mapping_key, mapping_value in item_mappings.items(): for mapping_key, mapping_value in item_mappings.items():
normalized_key = normalize_string(mapping_key, consider_azeri=True) normalized_key = normalize_string(mapping_key, consider_azeri=True)
if normalized_key == normalized_input: if normalized_key == normalized_input:

0
test_invoice.git/hooks/applypatch-msg.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/commit-msg.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/fsmonitor-watchman.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/post-update.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/pre-applypatch.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/pre-commit.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/pre-merge-commit.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/pre-push.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/pre-rebase.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/pre-receive.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/prepare-commit-msg.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/push-to-checkout.sample Executable file → Normal file
View File

0
test_invoice.git/hooks/update.sample Executable file → Normal file
View File