Skip to content

Instantly share code, notes, and snippets.

@rheaditi
Last active September 20, 2024 16:46
Show Gist options
  • Select an option

  • Save rheaditi/35a60f124530667870dd60afa2e52ffa to your computer and use it in GitHub Desktop.

Select an option

Save rheaditi/35a60f124530667870dd60afa2e52ffa to your computer and use it in GitHub Desktop.
Download all Payslips from Keka HR Software
/**
* This puppeteer script will automatically download all payslips for you, if your company uses Keka HR Software.
* I downloaded about 4 years of payslips in approximately 2 minutes! :P
*
* - Download this file and save it in some temporary directory.
* - Fill in the params wherever you see the ℹ️ in this file & save.
* - Install the dependency `puppeteer-core` (just run `npm init` and `npm install --save puppeteer-core@2` in your directory).
* - Requires NodeJS >= 12 (tested with v12.13.1), puppeteer-core@2 & Google Chrome browser.
* - Run: `node downloadPayslips.js`
* - The files are all saved to the browser's default download folder.
* - If there are any missing months, or if it happens to encounter any error, the script will take a screenshot & exit.
*
* License: MIT
*/
const puppeteer = require('puppeteer-core');
const data = {
/**
* 📝
* Manually copy the cookies over from your existing logged in session from your company's Keka HR Software.
* This allows access to your keka account so, goes without saying, to be careful with where these cookies are shared! 🤓
*/
cookies: [
{
"name": "__RequestVerificationToken",
"value": "<PASTE_VALUE_HERE>", // <-- PASTE HERE ℹ️
"httpOnly": true
},
{
"name": "ASP.NET_SessionId",
"value": "<PASTE_VALUE_HERE>", // <-- PASTE HERE ℹ️
"httpOnly": true
},
{
"name": ".AspNet.Cookies",
"value": "<PASTE_VALUE_HERE>", // <-- PASTE HERE ℹ️
"httpOnly": true,
"secure": true
}
],
/**
* 📝
* Parameters of which months of payslips to be downloaded. Both start & end periods are inclusive.
* Other parameters required by this script.
*/
params: {
startYear: 2020, startMonth: 1, // <-- set the start year and month here (months start from 1 (Jan)) ℹ️
endYear: 2020, endMonth: 3, // <-- set the end year and month here (months start from 1 (Jan)) ℹ️
chromePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' // path to Chrome browser installed on your system, the default here is for MacOS, change accordingly ℹ️
kekaDomain: 'example.keka.com', // the subdomain of your company on the keka.com website. ℹ️
},
};
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const getIterations = (options) => {
const { startYear, startMonth, endYear, endMonth } = options;
let totalIterations = [];
for (let year = startYear; year <= endYear; year++) {
let currentMonths = months.map((month, index) => ({
year,
month: index + 1,
name: month,
}));
if (year === startYear) {
currentMonths = currentMonths.filter(month => month.month >= startMonth);
}
if (year === endYear) {
currentMonths = currentMonths.filter(month => month.month <= endMonth);
}
totalIterations.push({
year,
months: currentMonths,
});
}
return totalIterations;
}
const iterations = getIterations(data.params);
const download = async () => {
const browser = await puppeteer.launch({
headless: false,
executablePath: data.params.chromePath,
devtools: false,
});
const page = await browser.newPage();
page.setViewport({
width: 1366,
height: 768,
deviceScaleFactor: 1
});
// setup
await page.setCookie(...data.cookies.map(cookie => ({ ...cookie, domain: data.params.kekaDomain })));
await page.goto(`https://${params.kekaDomain}/old/#/finances/mypay/payslips`, { waitUntil: 'networkidle2' });
for (let iteration of iterations) {
const { year, months } = iteration;
console.log(`running ${months.length} iterations for year ${year}`);
console.table(months);
// select year
await page.click('.pay-slip-year > div > button');
const [yearItem] = await page.$x(`//a[contains(.,${year})]`);
if (!yearItem) {
console.warn(`Unable to select year ${year}`);
page.screenshot({ path: `${year}--not-found.png` });
continue;
}
await yearItem.click();
await page.waitFor(5000);
// select each month
for (let month of months) {
// select month
const { name } = month;
const [monthButton] = await page.$x(`//a[contains(.,\'${name}\')]`);
if (!monthButton) {
console.warn(`Unable to select month ${year}-${month}`);
await page.screenshot({ path: `${year}-${name}-not-found.png` });
continue;
}
await monthButton.click();
await page.waitFor(1000);
// ensure correct month
const headings = await page.$x(`//h2[contains(.,'${name} ${year}')]`);
if (!headings.length) {
console.warn(`Month heading not found`);
await page.screenshot({ path: `${year}-${name}-heading-not-found.png` });
continue;
}
const [downloadButton] = await page.$x(`//a[contains(.,\'Download Payslip\')]`);
if (!downloadButton) {
console.warn(`Download button not found`);
await page.screenshot({ path: `${year}-${name}-download-button-not-found.png` });
continue;
}
// download it
await downloadButton.click();
await page.waitForResponse(response => {
const isDownload = response.url().indexOf('api/myfinances/payslip/export/') !== -1;
return isDownload && response.status() === 200;
});
await page.waitFor(750);
}
}
console.log('success!');
await browser.close();
};
try {
download();
} catch (e) {
console.error(e);
}
@nitish173
Copy link
Copy Markdown

Great!

@nitish173
Copy link
Copy Markdown

Line: 99, 100, need to replace params.kekaDomain with data.params.kekaDomain

@rheaditi
Copy link
Copy Markdown
Author

Line: 99, 100, need to replace params.kekaDomain with data.params.kekaDomain

Oops! Thanks a lot @nitish173. Fixed!

@sunil-sangwan
Copy link
Copy Markdown

Thanks Aditi for the script, it saved lot of time.

@rheaditi
Copy link
Copy Markdown
Author

rheaditi commented Jun 21, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment