// ==UserScript==
// @namespace virtonomica
// @name Virtonomica: Quick Price Set
// @description:en Quick installation prices of goods in the trading hall.
// @description:ru Быстрая установка цены товаров в торговом зале.
// @include https://virtonomica*.*/*/main/unit/view/*/trading_hall
// @include https://virtonomica.ru/*/main/unit/view/*/trading_hall
// @version 97
// @grant AntiStream
// @description Quick installation prices of goods in the trading hall.
// ==/UserScript==
(function(window) {
let run = function() {
const VER = 97;
//
// Localization
//
let en_EN = {
"minPriceCost": "% Minimum Price of Cost (if there is)",
"targetPrice": "% Price of city price",
"storeList": "Store List:",
"addStoreToList": "Add Store To List",
"removeStoreFromList": "Remove Store From List",
"calcStorePrice": "Сalc Prices",
"itsMagicFrame": "IT'S MAGIC IFRAME, RUN! (Сalculate the price for all stores from the list!)",
"saveStoreConfig": "Save Config",
"wait": "Wait...",
"calcGroupPrice": "Сalc Group Price",
"minPriceOff": "MinPrice(0=off)",
"itemMul": "ItemPriceMul",
"calcItemPrice": "Сalc Price",
"rulesOfPricing": "Rules of pricing:",
"rule1Dis": "IF market share less X%, THAT price is multiplied by Y",
"rule2Dis": "IF market share greater X%, THAT price is multiplied by Y",
"rule3Dis": "The adjustment of prices depending on quality of item and city: P * ((IQ / CQ) ^ (1 / X))",
"rule4Dis": "The adjustment of prices depending on brand of item and city: P * (((1+IB) / (1+CB)) ^ (1 / X))",
"addRule": "Add rule: ",
"deleteRule": "Delete rule",
};
let ru_RU = {
"minPriceCost": "% минимальной цены от себестоимости (если есть)",
"targetPrice": "% цены от цены города",
"storeList": "Список магазинов:",
"addStoreToList": "Добавить магазин в список",
"removeStoreFromList": "Удалить магазин из списка",
"calcStorePrice": "Вычислить цену всего",
"itsMagicFrame": "IT'S MAGIC IFRAME, RUN! (Вычислить цену для всего во всех магазинах из списка)",
"saveStoreConfig": "Сохранить параметры",
"wait": "Жди...",
"calcGroupPrice": "Вычислить цены группы",
"minPriceOff": "МинЦена(0=выкл)",
"itemMul": "МножетельЦены",
"calcItemPrice": "Вычислить цену",
"rulesOfPricing": "Правила формирования цены:",
"rule1Dis": "Если доля рынка меньше X%, то цена умножается на Y",
"rule2Dis": "Если доля рынка больше X%, то цена умножается на Y",
"rule3Dis": "Корректировка цены в зависимости от качества предмета и города: P * ((IQ / CQ) ^ (1 / X))",
"rule4Dis": "Корректировка цены в зависимости от бренда предмета и города: P * (((1+IB) / (1+CB)) ^ (1 / X))",
"addRule": "Добавить правило: ",
"deleteRule": "Удалить правило",
};
let loc = (navigator.language || navigator.languages[0]) == "ru" ? ru_RU : en_EN;
//
// qps_mainCont
//
let $tbody = $("table.grid:eq(0) > tbody:eq(0)");
$tbody.parent().before(`
<div class="qps_mainCont">
<h1>
<a href="https://greasyfork.org/ru/scripts/8095-virtonomica-quick-price-set">Virtonomica: Quick Price Set</a> ${VER}
</h1>
</div>
`);
let $qps_mainCont = $(".qps_mainCont");
const thisStoreUrl = window.location.toString();
//
// PRICE
//
$qps_mainCont.append(`
<div>
<div>
${loc["rulesOfPricing"]}
</div>
<div>
<input id='qps_minCostPrise' type='text' value='200'>${loc["minPriceCost"]}
</div>
<div style="margin-top: 2px;">
<input id='qps_targetPrice' type='text' value='110'>${loc["targetPrice"]}
</div>
<div style="margin-top: 2px; background-color: #DADADA;">
<div id="qps_rulesList">
</div>
<div style="margin-top: 2px;">
<input id='qps_addRule' class='qps_button' type='button' value='${loc["addRule"]}'><select id="qps_ruleType">
<option value="rule1">${loc["rule1Dis"]}</option>
<option value="rule2">${loc["rule2Dis"]}</option>
<option value="rule3">${loc["rule3Dis"]}</option>
<option value="rule4">${loc["rule4Dis"]}</option>
</select>
</div>
</div>
</div>
`);
let $qps_minCostPrise = $("#qps_minCostPrise");
let $qps_targetPrice = $("#qps_targetPrice");
let $qps_rulesList = $("#qps_rulesList");
let addRule = function(obj) {
let ruleType = obj.type;
if (["rule1", "rule2"].indexOf(ruleType) > -1) {
$qps_rulesList.append(`
<div rule_type="${ruleType}" style="margin-left: 2px;">
<div>
${
loc[ruleType+"Dis"].
replace("X", `<input class="qpsDol" type="text" size=4 value="${obj.dol}">`).
replace("Y", `<input class="qpsMul" type="text" size=4 value="${obj.mul}">`)
}
<input type="button" value="${loc["deleteRule"]}" onclick="$(this).parent().parent().remove()">
</div>
</div>
`);
} else if (["rule3", "rule4"].indexOf(ruleType) > -1) {
$qps_rulesList.append(`
<div rule_type="${ruleType}" style="margin-left: 2px;">
<div>
${loc[ruleType+"Dis"].replace("X", `<input class="qpsX" type="text" size=4 value="${obj.x}">`)}
<input type="button" value="${loc["deleteRule"]}" onclick="$(this).parent().parent().remove()">
</div>
</div>
`);
}
};
$("#qps_addRule").click(function() {
let ruleType = $("#qps_ruleType").val();
if (["rule1", "rule2"].indexOf(ruleType) > -1) {
thisStoreData.rulesList.push({
type: ruleType,
dol: "100",
mul: "1.0"
});
addRule({
type: ruleType,
dol: "100",
mul: "1.0"
});
} else if (["rule3", "rule4"].indexOf(ruleType) > -1) {
thisStoreData.rulesList.push({
type: ruleType,
x: "5",
});
addRule({
type: ruleType,
x: "5",
});
}
});
/* killer feature, unbalance! =)
//
// STORE LIST
//
$qps_mainCont.append(`
<div style="margin-top: 10px;">
<div>
${loc["storeList"]}
</div>
<div id='qps_storeList' style="margin-left: 4px; margin-top: 4px;">
</div>
<div style="margin-top: 4px;">
<input id='qps_addStoreToList' class='qps_button' type='button' value='${loc["addStoreToList"]}'>
</div>
<div>
<input id='qps_removeStoreFromList' class='qps_button' type='button' value='${loc["removeStoreFromList"]}'>
</div>
</div>
`);
let storeList = JSON.parse(localStorage.getItem("storeList") || `{}`);
let $qps_storeList = $("#qps_storeList");
let $qps_addStoreToList = $("#qps_addStoreToList");
let $qps_removeStoreFromList = $("#qps_removeStoreFromList");
let drawStoreList = function() {
$qps_storeList.empty();
for (let store in storeList) {
$qps_storeList.append(`
<div store="${store}">
${store}
</div>
`);
}
};
drawStoreList();
$qps_addStoreToList.click(function() {
storeList[thisStoreUrl] = thisStoreUrl;
localStorage.setItem("storeList", JSON.stringify(storeList));
drawStoreList();
});
$qps_removeStoreFromList.click(function() {
delete storeList[thisStoreUrl];
localStorage.setItem("storeList", JSON.stringify(storeList));
drawStoreList();
});
//
// MAGIC IFRAME
//
$qps_mainCont.append(`
<div style="margin-top: 10px;">
<iframe id='qps_iframe' style="width:0;height:0;border:0;border:none;"></iframe>
<input id='qps_iframeMagic' class='qps_button' type='button' value="${loc["itsMagicFrame"]}">
</div>
`);
let $qps_iframe = $("#qps_iframe");
let $qps_iframeMagic = $("#qps_iframeMagic");
$qps_iframeMagic.click(function() {
let $this = $(this);
$this.attr("disabled", "disabled");
$this.val(loc["wait"]);
let $MIF = $qps_iframe;
let stores = [];
for (let store in storeList) {
stores.push(store);
}
let index = -1;
let endMagic = function() {
window.location = window.location;
};
let nextStore = function() {
index++;
let firstLoad = true;
let store = stores[index];
$MIF.load(function() {
if (firstLoad) {
firstLoad = false;
$qps_storeList.find(`div[store="${store}"]`).css("background-color", "yellow");
$MIF.contents().find("#qps_calcStoreButton").click();
setTimeout(function() {
$MIF.contents().find("input[name='setprice']").click();
}, 300);
} else {
setTimeout(function() {
$qps_storeList.find(`div[store="${store}"]`).css("background-color", "green");
nextStore();
}, 3000);
}
});
if (stores[index]) {
$MIF.attr("src", stores[index]);
} else {
endMagic();
}
};
nextStore();
});
*/
//
// SET STORE PRICE
//
$qps_mainCont.append(`
<div style="margin-top: 2px;" class="qps_footer">
<input id='qps_calcStoreButton' class='qps_button' type='button' value='${loc["calcStorePrice"]}'>
</div>
`);
let $qps_footer = $qps_mainCont.children(".qps_footer:eq(0)");
$("#qps_calcStoreButton").click(function() {
setPrice(-1, -1);
});
//
// Store Config and Set Price
//
$qps_footer.append(`
<input id='qps_saveStoreConfig' class='qps_button' type='button' value='${loc["saveStoreConfig"]}'>
<input id="qps_savePrices" class='qps_button' type='button' value='${$("input[name='setprice']").val()}'>
`);
let thisStoreData = JSON.parse(localStorage.getItem(thisStoreUrl) || `{}`);
thisStoreData.itemsMinPrice = thisStoreData.itemsMinPrice || {};
thisStoreData.itemsPriceMult = thisStoreData.itemsPriceMult || {};
thisStoreData.rulesList = thisStoreData.rulesList || [];
if (thisStoreData.targetPriceType) {
delete thisStoreData.targetPriceType;
}
$("#qps_savePrices").click(function() {
$("input[name='setprice']").click();
});
if (thisStoreData.minPrice) {
$qps_minCostPrise.val(thisStoreData.minPrice);
}
if (thisStoreData.targetPrice) {
$qps_targetPrice.val(thisStoreData.targetPrice);
}
if (thisStoreData.rulesList.length > 0) {
for (let rule of thisStoreData.rulesList) {
addRule(rule);
}
} else {
addRule({type: "rule2",dol: "90",mul: "1.05"});
addRule({type: "rule1",dol: "30",mul: "0.95"});
}
$("#qps_saveStoreConfig").click(function() {
thisStoreData = {};
thisStoreData.minPrice = $qps_minCostPrise.val();
thisStoreData.targetPrice = $qps_targetPrice.val();
thisStoreData.itemsMinPrice = {};
thisStoreData.itemsPriceMult = {};
$tbody.children("tr").each(function() {
let $tr = $(this);
if ($tr.children("td").length > 1) {
let itemType = $tr.children("td:eq(2)").attr("title").replace(/^(.*) \(.*$/, "$1");
thisStoreData.itemsMinPrice[itemType] = $tr.children("td:eq(9)").find(".qps_minPrice:eq(0)").val();
thisStoreData.itemsPriceMult[itemType] = $tr.children("td:eq(9)").find(".qps_priceMult:eq(0)").val().replace(",", ".");
}
});
let tmpRls = [];
$qps_rulesList.children().each(function() {
$this = $(this);
let ruleType = $this.attr("rule_type");
if (["rule1", "rule2"].indexOf(ruleType) > -1) {
tmpRls.push({
type: ruleType,
dol: $this.find(".qpsDol").val(),
mul: $this.find(".qpsMul").val().replace(",", ".")
});
} else if (["rule3", "rule4"].indexOf(ruleType) > -1) {
tmpRls.push({
type: ruleType,
x: $this.find(".qpsX").val().replace(",", "."),
});
}
});
thisStoreData.rulesList = tmpRls;
thisStoreData.ver = VER;
localStorage.setItem(thisStoreUrl, JSON.stringify(thisStoreData));
});
//
// SET PRICE
//
let setPrice = function(group, item) {
if (!Number.isFinite($qps_minCostPrise.val() - 0)) {
$qps_minCostPrise.val($qps_minCostPrise.val() + "ERR!");
return;
}
if (!Number.isFinite($qps_targetPrice.val() - 0)) {
$qps_targetPrice.val($qps_targetPrice.val() + "ERR!");
return;
}
let minPriceMultiplier = $qps_minCostPrise.val() * 0.01;
let targetPriceMultiplier = $qps_targetPrice.val() * 0.01;
let groupId = 0;
let itemId = 0;
$tbody.children("tr").each(function() {
let $tr = $(this);
if ($tr.children("td").length === 1) {
groupId++;
} else if ($tr.children("td").length > 1) {
itemId++;
if ((group === -1 || group === groupId) && (item === -1 || item === itemId)) {
let $td10 = $tr.children("td:eq(9)"); // price input
let $priceInput = $td10.children("input:eq(0)");
const itemType = $tr.children("td:eq(2)").attr("title").replace(/^(.*) \(.*$/, "$1").trim();;
let itemQuality = $tr.children("td:eq(6)").text().trim() - 0;
let itemBrand = $tr.children("td:eq(7)").text().trim() - 0;
let itemCost = $tr.children("td:eq(8)").text().trim().replace(" ", "").replace("$", "") - 0;
const cityPrice = $tr.children("td:eq(11)").text().trim().replace(" ", "").replace("$", "") - 0;
const cityQuality = $tr.children("td:eq(12)").text().trim() - 0;
const cityBrand = $tr.children("td:eq(13)").text().trim() - 0;
const marketShare = $tr.children("td:eq(10)").text().trim().replace(" ", "").replace("%", "") - 0;
let currentPrice = $td10.children("input:eq(0)").val().trim().replace(" ", "") - 0;
let targetPrice = 0;
let minPrice = 0;
let maxPrice = Number.POSITIVE_INFINITY;
if (itemCost.toString() !== "NaN") {
minPrice = itemCost * minPriceMultiplier;
} else {
itemCost = cityPrice;
itemQuality = cityQuality;
}
let itemMinPrice = $td10.find("input.qps_minPrice").val();
itemMinPrice = Number.isFinite(itemMinPrice - 0) ? (itemMinPrice - 0) : 0;
if (itemMinPrice > 0) {
minPrice = itemMinPrice;
}
targetPrice = cityPrice * targetPriceMultiplier;
let itemPriceMult = $td10.find("input.qps_priceMult").val().replace(",", ".")-0;
if (Number.isFinite(itemPriceMult)) {
targetPrice *= itemPriceMult;
}
// Rules
$qps_rulesList.children().each(function() {
$this = $(this);
let ruleType = $this.attr("rule_type");
if (["rule1", "rule2"].indexOf(ruleType) > -1) {
let rDol = $this.find(".qpsDol").val().replace(",", ".") - 0;
let rMul = $this.find(".qpsMul").val().replace(",", ".") - 0;
if (Number.isFinite(rDol) && Number.isFinite(rMul)) {
if (
(ruleType == "rule1" && marketShare < rDol) ||
(ruleType == "rule2" && marketShare > rDol)
) {
targetPrice *= rMul;
}
}
} else if (["rule3", "rule4"].indexOf(ruleType) > -1) {
let rX = $this.find(".qpsX").val().replace(",", ".") - 0;
if (Number.isFinite(rX)) {
if (ruleType == "rule3") {
targetPrice *= Math.pow(itemQuality / cityQuality, 1 / rX);
} else if (ruleType == "rule4") {
targetPrice *= Math.pow((1+itemBrand) / (1+cityBrand), 1 / rX);
}
}
}
});
if (targetPrice < minPrice) {
targetPrice = minPrice;
}
if (targetPrice > maxPrice) {
targetPrice = maxPrice;
}
$td10.children("input:eq(0)").val(targetPrice.toFixed(2));
}
}
});
};
let groupId = 0;
let itemId = 0;
$tbody.children("tr").each(function() {
let $tr = $(this);
if ($tr.children("td").length === 1) {
(function(groupId) {
$tr.append(`
<div class="qps_cont">
<input class='qps_button' type='button' value='${loc["calcGroupPrice"]}'>
</div>
`);
$tr.find("input[class='qps_button']").click(function() {
setPrice(groupId, -1);
});
})(++groupId);
} else if ($tr.children("td").length > 1) {
let $td10 = $tr.children("td:eq(9)");
(function(groupId, itemId) {
$td10.append(`
<div class="qps_cont">
<div>
<div>
Min<input class="qps_minPrice" size=11 placeholder="${loc["minPriceOff"]}" title="${loc["minPriceOff"]}" type="text" value="0">
</div>
<div>
Mul<input class="qps_priceMult" size=11 placeholder="${loc["itemMul"]}" title="${loc["itemMul"]}" type="text" value="1.0">
</div>
</div>
<div>
<input class='qps_button' type='button' value='${loc["calcItemPrice"]}'>
</div>
</div>
`);
$td10.find("input[class='qps_button']").click(function() {
setPrice(groupId, itemId);
});
let itemType = $tr.children("td:eq(2)").attr("title").replace(/^(.*) \(.*$/, "$1");
if (thisStoreData.itemsMinPrice[itemType]) {
$td10.find(".qps_minPrice:eq(0)").val(thisStoreData.itemsMinPrice[itemType] || "0");
$td10.find(".qps_priceMult:eq(0)").val(thisStoreData.itemsPriceMult[itemType] || "1.0");
}
})(groupId, ++itemId);
}
});
};
$("head").append(
`<style>
.qps_button {
background-color: green;
color: white;
}
.qps_mainCont {
background-color: #81cfef;
}
.qps_cont {
background-color: #81cfef;
}
</style>`);
let script = document.createElement("script");
script.textContent = '(' + run.toString() + ')();';
document.documentElement.appendChild(script);
})(window);