Jak wpisać koszty do 500 produktów, nie klikając 500 razy
Rdzeń WooCommerce daje jedno pole kosztu na produkt i żadnej edycji masowej. Przy pięciuset produktach to nie jest wieczór pracy, tylko powód, żeby funkcji nie włączyć w ogóle. Tu jest droga z arkusza, przez CSV, jednym skryptem.
Aktualizacja 2026-09-06
Ta strona zakłada, że funkcja kosztu własnego sprzedaży jest już włączona. Jeśli nie — przewodnik po włączeniu jest krótszy niż ta strona i trzeba go przeczytać pierwszy, bo dopóki przełącznik jest wyłączony, wpisane koszty nie trafią do żadnego zamówienia.
Najpierw policz, ilu produktów naprawdę potrzebujesz
Odruch mówi „uzupełnię wszystkie”. To najdroższa możliwa kolejność, bo pierwsze osiemdziesiąt procent wartości wpisuje się w pierwszej godzinie, a reszta zabiera resztę tygodnia.
Raport zysku nie potrzebuje kompletu. Potrzebuje pokrycia — jakiej części przychodu dotyczy znany koszt. Sklep, który zna koszt pięćdziesięciu produktów robiących 80% obrotu, ma raport użyteczny; sklep, który zna koszt czterystu produktów z ogona i nie zna kosztu dziesięciu bestsellerów, nie ma nic.
Kolejność jest więc jedna: posortuj katalog po przychodzie i idź od góry, nie alfabetycznie i nie po dacie dodania. Zatrzymaj się, gdy pokrycie przestanie rosnąć w tempie, które usprawiedliwia wpisywanie.
Uzupełnienie kosztu nie zmienia zamówień, które już są w bazie. Koszt zapisuje się na pozycji zamówienia w chwili sprzedaży, więc dzisiejszy wpis działa od dzisiaj w przód. To nie jest powód, żeby zwlekać — to powód, żeby zrobić to w tym tygodniu, a nie w przyszłym kwartale.
Arkusz — trzy kolumny i jedna decyzja
Pobierz szablon CSV albo zrób własny; liczy się układ:
id,sku,cost
1042,,41.50
,KOSZULKA-XL-CZARNA,24.00
1198,,
idalbosku— jedno z dwóch, nie oba naraz. Skrypt bierzeid, a gdy jest puste, szuka produktu posku.cost— koszt netto za jedną sztukę, kropka jako separator dziesiętny. Bez waluty, bez spacji, bez symbolu waluty.- Puste
costznaczy „nie wiem” i skrypt taki wiersz pomija. To nie jest uprzejmość wobec danych, tylko konieczność: zapisane zero i brak kosztu są w bazie WooCommerce tym samym, więc wpisanie zera „na razie” kasuje informację, zamiast ją dodać.
Decyzja, którą trzeba podjąć raz, przed pierwszym wierszem: co u Ciebie znaczy „koszt”. Sama cena zakupu? Z transportem do magazynu? Z cłem, opakowaniem, prowizją platformy? Każda z tych odpowiedzi jest poprawna, żadna nie jest poprawna w połowie katalogu. Marża policzona z dwóch różnych definicji kosztu nie jest porównywalna z niczym — także sama ze sobą sprzed miesiąca. Zapisz wybór w pierwszym wierszu arkusza, żeby za pół roku nie zgadywać.
Skąd wziąć listę produktów: WooCommerce → Produkty → Eksportuj daje CSV z kolumnami ID
i SKU. Zostaw te dwie, dodaj trzecią i wypełniaj w arkuszu, gdzie widać wszystko naraz.
Skrypt — jedna pętla po pliku
Poniższy skrypt uruchamia się przez WP-CLI. Czyta CSV, znajduje produkt, zapisuje koszt i wypisuje, co zrobił. Zaczyna w trybie na sucho — pierwsza linijka o tym decyduje.
<?php
/**
* Uzupelnia natywne pole kosztu wlasnego sprzedazy z pliku CSV (id,sku,cost).
* Uruchomienie: wp eval-file uzupelnij-koszty.php koszty.csv
*/
$NA_SUCHO = true; // <- zmien na false dopiero po udanej probie
$plik = $args[0] ?? 'koszty.csv';
$uchwyt = fopen( $plik, 'r' );
if ( ! $uchwyt ) {
WP_CLI::error( "Nie moge otworzyc pliku: {$plik}" );
}
fgetcsv( $uchwyt ); // wiersz naglowka: id,sku,cost
$zapisane = 0;
$pominiete = 0;
$bledy = 0;
while ( ( $wiersz = fgetcsv( $uchwyt ) ) !== false ) {
list( $id, $sku, $koszt ) = array_pad( $wiersz, 3, '' );
$koszt = trim( str_replace( ',', '.', $koszt ) );
if ( '' === $koszt ) { // puste to nie zero — pomijamy
$pominiete++;
continue;
}
if ( ! is_numeric( $koszt ) || (float) $koszt < 0 ) {
WP_CLI::warning( "Zla wartosc kosztu: '{$koszt}' (id={$id}, sku={$sku})" );
$bledy++;
continue;
}
$id = (int) $id;
if ( ! $id && $sku ) {
$id = wc_get_product_id_by_sku( trim( $sku ) );
}
$produkt = $id ? wc_get_product( $id ) : null;
if ( ! $produkt ) {
WP_CLI::warning( "Nie znalazlem produktu (id={$id}, sku={$sku})" );
$bledy++;
continue;
}
if ( $NA_SUCHO ) {
WP_CLI::log( sprintf( '[na sucho] %d %s: %s -> %s',
$produkt->get_id(), $produkt->get_name(),
var_export( $produkt->get_cogs_value(), true ), $koszt ) );
} else {
$produkt->set_cogs_value( (float) $koszt );
$produkt->save();
WP_CLI::log( sprintf( '%d %s = %s', $produkt->get_id(), $produkt->get_name(), $koszt ) );
}
$zapisane++;
}
fclose( $uchwyt );
WP_CLI::success( sprintf( '%s: %d, pominiete (pusty koszt): %d, bledy: %d',
$NA_SUCHO ? 'Do zapisania' : 'Zapisane', $zapisane, $pominiete, $bledy ) );
Zapisz jako uzupelnij-koszty.php obok pliku koszty.csv i uruchom:
wp eval-file uzupelnij-koszty.php koszty.csv
Skrypt jest tu do przeczytania, a nie do pobrania jednym kliknięciem — celowo. Zapisuje do Twojej bazy, więc powinien przejść przez Twoje oczy, zanim przejdzie przez Twoje produkty.
Dlaczego nie pętla w powłoce. Kuszące wp eval wołane w pętli po wierszach uruchamia cały
WordPress raz na produkt: przy pięciuset produktach to kilkanaście minut zamiast kilku sekund
i pięćset okazji, żeby coś się wysypało w połowie. Jeden skrypt, jeden start.
Jeśli nie masz SSH. Ta sama para metod — set_cogs_value() i save() — działa wszędzie, gdzie
umiesz uruchomić PHP w kontekście WordPressa. REST API niesie tę samą wartość na zasobach
products i variations, jako cost_of_goods_sold z polami values, total_value i — przy
wariantach — defined_value_is_additive. Dwie rzeczy warto wiedzieć, zanim napiszesz pętlę:
effective_value i total_value są tylko do odczytu, a pusta tablica values znaczy koszt zero,
ze wszystkim, co z tego wynika w dalszej części tej strony. Odczytaj najpierw jeden produkt
(GET /wp-json/wc/v3/products/<id>) i sprawdź kształt na własnej wersji.
Próba, której nie warto pomijać
- Kopia bazy. Nie „eksport produktów” — kopia. Skrypt zapisuje, a zapis produktu potrafi dotknąć więcej niż jednego pola.
- Dziesięć wierszy najpierw. Skopiuj dziesięć linii do
proba.csvi puść na nich całość: na sucho, potem naprawdę, potem zajrzyj w panel na te dziesięć produktów. - Dopiero potem
$NA_SUCHO = falsena pełnym pliku. - Sprawdzenie po fakcie — nie w arkuszu, tylko w sklepie. Złóż jedno testowe zamówienie na produkt z wpisanym kosztem i zobacz, czy zamówienie zapisało koszt. To jedyny test, który sprawdza cały łańcuch, a nie samo pole w kartotece.
Warianty — skrypt ich nie rozstrzygnie za Ciebie
Wariant dziedziczy koszt produktu nadrzędnego, dopóki nie ma własnego, a gdy ma — jeden przełącznik decyduje, czy własny koszt zastępuje koszt rodzica, czy jest do niego doliczany. To jest decyzja o znaczeniu liczby, nie o jej wartości, więc skrypt jej nie podejmuje.
Praktycznie: wpisuj hurtem koszty produktów nadrzędnych, a warianty ruszaj tylko tam, gdzie naprawdę różnią się kosztem — i wtedy ustaw tryb w edytorze produktu, świadomie, na oczach. Koszulka z kosztem 24 i wariant „z haftem” w trybie doliczanym z kosztem 3 daje 27; ten sam wariant w trybie zastępującym daje 3. Szczegóły w kosztach w wariantach.
Uwaga przy sprawdzaniu wyników: kolumna pomocnicza WooCommerce trzyma wartość wpisaną, a nie efektywną, więc wariant spokojnie dziedziczący koszt rodzica wygląda w zapytaniu SQL jak wariant bez kosztu. Lista „produkty bez kosztu” zbudowana wprost na tej kolumnie będzie pełna fałszywych alarmów.
Czego to nie naprawi
- Historii. Zamówienia sprzed wpisania kosztu zostaną bez kosztu na zawsze; nie ma uzupełniania wstecz i nie jest to przeoczenie.
- Gratisów. Produkt, który naprawdę kosztuje zero, zapisze się jako brak kosztu, bo rdzeń kasuje wartość zamiast zapisać zero. Zostanie na liście braków i nic z tym nie zrobisz.
- Zwrotów. Wystawienie zwrotu każe WooCommerce przeliczyć koszt każdej pozycji pierwotnego zamówienia dzisiejszym kosztem produktu — czyli świeżo wpisane koszty potrafią przepisać historię, której dotyczą. Co się wtedy dzieje.
- Kosztów zmiennych. Wysyłka, prowizje bramek płatności i zwroty do dostawcy nie są kosztem towaru i pole kosztu ich nie dotyczy. Zysk brutto to nie zysk netto i nie udajemy, że jest.
Lista kontrolna
- Funkcja kosztu włączona — inaczej cała reszta nic nie da.
- Jedna definicja kosztu, zapisana w arkuszu.
- Eksport produktów →
id,sku, pusta kolumnacost. - Sortowanie po przychodzie, uzupełnianie od góry.
- Kopia bazy, próba na dziesięciu wierszach, dopiero potem całość.
- Testowe zamówienie na sprawdzenie całego łańcucha.
- Warianty osobno i świadomie — tryb zastępowania albo doliczania.
Jeżeli któryś krok zachowa się u Ciebie inaczej, niż tu napisano, warto o tym napisać — te strony są poprawiane, kiedy okazują się nieprawdziwe.